Search code examples
assemblydosx86-16emu8086

Difficulty completing the snake game with assembly language


I was able to create a game with assembly language and using the star, which ends like a snake game when it hits the wall, but I want to add another thing to it and that the game ends when the star hits itself. And I have a problem solving part, I wanted you to help.

gotoxy macro x,y
     mov dl,x
     mov dh,y
     mov bh,0
     mov ah,2
     int 10h
endm 

printchar macro n
     mov dl,n
     mov ah,2
     int 21h
endm 

printstring macro n
     lea dx,n
     mov ah,9
     int 21h
endm

.model small
.stack 64
.data
    x db 40
    y db 12
    deltax db 0
    deltay db -1 
    msg1 DB "GAME OVER !!!$"
.code
main:            
     mov ax,@data
     mov ds,ax
     gotoxy x,y     
     printchar '*'

     mov ah,7
     int 21h
l1:  
     gotoxy x,y
     mov al,x
     add al,deltax
     mov x,al       
     mov al,y
     add al,deltay
     mov y,al       
     
     gotoxy x,y     
     printchar '*'

     mov ah,0bh     
     int 21h
     cmp al,0     
     je l2          
     mov ah,0
     int 16h        
     cmp al,'w'     
     je L_W
     cmp al,'s'
     je L_S
     cmp al,'a' 
     je L_A
     cmp al,'d'     
     je l_D 
     jmp l2
L_W:                
     mov deltax,0
     mov deltay,-1
     jmp L2
L_S:
     mov deltax,0
     mov deltay,1
     jmp L2
L_A:
     mov deltax,-1
     mov deltay,0
     jmp L2
L_D:
     mov deltax,1
     mov deltay,0
     
L2:     
     cmp y,0      
     je LEND
     cmp y,23
     je LEND
     cmp x,0
     je LEND
     cmp x,79
     je LEND
     jmp l1

LEND:    
     gotoxy 35,12      
     printstring msg1
     mov ah,7
     int 21h
     
     mov ah,4ch
     int 21h
end main

picture from run in emu8086


Solution

  • printchar macro n
      mov dl,n
      mov ah,2
      int 21h
    endm
    

    Your printchar macro uses the DOS.PrintChar function 02h. For the purpose of your snake game, it's not useful and could sometimes be harmful that this DOS function also emits a carriage return / linefeed. You should use the BIOS.WriteCharacter function 0Ah instead.

    printchar macro n
      mov cx, 1
      mov bh, 0
      mov al, n
      mov ah, 0Ah  ; BIOS.WriteCharacter
      int 10h
    endm
    

    Colliding with a star

    To detect a collision all it takes is reading from the video memory using BIOS.ReadCharacterAndAttribute function 08h. You do this right between going to the next position (gotoxy) and displaying another star (printchar).

    gotoxy x,y
    
    mov bh, 0
    mov ah, 08h   ; BIOS.ReadCharacterAndAttribute
    int 10h       ; -> AX
    cmp ax, 0720h ; Is this position unoccupied? (0720h means a WhiteOnBlack space character)
    jne LEND      ; No
    
    printchar '*'
    

    Crashing into the border

    If you would draw the rectangle (0,0) - (79,23), you would not have to verify exceeding the coordinates at label L2 because the above collision detection would also take care of that.

    Next is a quick solution to draw a white rectangle:

    mov dx, 174Fh  ; (79,23)
    xor cx, cx     ; (0,0)
    mov bh, 77h    ; WhiteOnWhite
    mov ax, 0600h  ; BIOS.ClearWindow
    int 10h
    mov dx, 164Eh  ; (78,22)
    mov cx, 0101h  ; (1,1)
    mov bh, 07h    ; WhiteOnBlack
    mov ax, 0600h  ; BIOS.ClearWindow
    int 10h
    

    One final tip

    Be consistent about retrieving a key from the user. Your program mixes the DOS function 07h and the Keyboard BIOS function 00h. I would suggest you use the Keyboard BIOS function all the time.


    [edit]
    Your completed code with corrections and even more improvements.
    Please note that I have increased the size for the stack. 64 bytes is a bit small and can easily get you into trouble with certain BIOS and/or DOS system calls.

    gotoxy macro x, y
      mov dl, x
      mov dh, y
      mov bh, 0
      mov ah, 02h     ; BIOS.SetCursor
      int 10h
    endm 
        
    printchar macro n
      mov cx, 1
      mov bh, 0
      mov al, n
      mov ah, 0Ah     ; BIOS.WriteCharacter
      int 10h
    endm
        
    printstring macro n
      lea dx, n
      mov ah, 09h     ; DOS.PrintString
      int 21h
    endm
        
    .model small
    .stack 512
    .data
      x     db 40
      y     db 12
      delta dw 0FF00h ; deltax = 0  deltay = -1 
      msg1  db "GAME OVER !!!$"
    .code
    main:            
      mov ax, @data
      mov ds, ax
    
      mov dx, 174Fh   ; (79,23)
      xor cx, cx      ; (0,0)
      mov bh, 77h     ; WhiteOnWhite
      mov ax, 0600h   ; BIOS.ClearWindow
      int 10h
      mov dx, 164Eh   ; (78,22)
      mov cx, 0101h   ; (1,1)
      mov bh, 07h     ; WhiteOnBlack
      mov ax, 0600h   ; BIOS.ClearWindow
      int 10h
    
      gotoxy x, y
      printchar '*'
        
      mov ah, 00h     ; BIOS.GetKey
      int 16h         ; -> AX
    
    Again:  
      mov ax, delta
      add x, al
      add y, ah
      gotoxy x, y
    
      mov bh, 0
      mov ah, 08h     ; BIOS.ReadCharacterAndAttribute
      int 10h         ; -> AX
      cmp ax, 0720h   ; Is this position unoccupied?
      jne Done        ; No
    
      printchar '*'
        
      mov ah, 01h     ; BIOS.TestKey
      int 16h         ; -> AX ZF
      jz  Again       ; No key available
      mov ah, 00h     ; BIOS.GetKey
      int 16h         ; -> AX
      mov bx, 0FF00h  ; deltax = 0  deltay = -1
      cmp al, 'w'     ; Up
      je  SetDelta
      mov bx, 0100h   ; deltax = 0  deltay = 1
      cmp al, 's'     ; Down
      je  SetDelta
      mov bx, 00FFh   ; deltax = -1  deltay = 0
      cmp al, 'a'     ; Left
      je  SetDelta
      mov bx, 0001h   ; deltax = 1  deltay = 0
      cmp al, 'd'     ; Right
      jne Again
    SetDelta:
      mov delta, bx
      jmp Again
        
    Done:    
      gotoxy 35, 12
      printstring msg1
      mov ah, 00h     ; BIOS.GetKey
      int 16h         ; -> AX
      mov ax ,4C00h   ; DOS.Terminate
      int 21h
    end main