'Tic-tac-toe game does not terminate in case of a draw

I am making a tic-tac-toe game using assembly language. It does not terminate in case of a draw. How can I rectify it?
Below is the code:

data segment       
    new_line db 13, 10, "$"
    
    game_draw db "_|_|_", 13, 10
              db "_|_|_", 13, 10
              db "_|_|_", 13, 10, "$"    
                  
    game_pointer db 9 DUP(?)  
    
    win_flag db 0 
    player db "0$" 
    
    game_over_message db "Game ended", 13, 10, "$"    
    game_start_message db "Welcome", 13, 10, "$"
    player_message db "PLAYER $"   
    win_message db " WIN!$"   
    type_message db "TYPE A POSITION: $"  
    tra db 'want to play again? (y/n):$'
    drw db "the game is draw! $"
      
    
    

ends

stack segment
    dw   128  dup(?)
ends         

extra segment
    
ends
  
code segment
   
start:
    ; set segment registers
    mov     ax, data
    mov     ds, ax
    mov     ax, extra
    mov     es, ax

    ; game start   
    call    set_game_pointer    
            
main_loop:  
    call    clear_screen   
    
    lea     dx, game_start_message 
    call    print
    
    lea     dx, new_line
    call    print                      
    
    lea     dx, player_message
    call    print
    lea     dx, player
    call    print  
    
    lea     dx, new_line
    call    print    
    
    lea     dx, game_draw
    call    print    
    
    lea     dx, new_line
    call    print    
    
    lea     dx, type_message    
    call    print            
                        
    ; read draw position                   
    call    read_keyboard
                       
    ; calculate draw position                   
    sub     al, 49               
    mov     bh, 0
    mov     bl, al 
    call update_draw
   
                                   
                                 
                                    
                                                          
    call    check  
                       
    ; check if game ends                   
    cmp     win_flag, 1 
    je     game_over ;STEPH CHANGES  
    
    call    change_player 
            
   jmp      main_loop 


change_player:   
    lea     si, player    
    xor     ds:[si], 1 
    
    ret
  update_draw:
    mov     bl, game_pointer[bx]
    mov     bh, 0
    
    lea     si, player
    
    cmp     ds:[si], "0"
    je      draw_x 
        
                  
    cmp     ds:[si], "1"
    je      draw_o              
                  
    draw_x:
    mov     cl, "x"
    jmp     update 
    

    draw_o:          
    mov     cl, "o"  
    jmp     update    
     
                  
                   
    update:         
    mov     ds:[bx], cl 
    
    
    
     

                        

    
      
    ret     
 

       
       
check:
    call    check_line
    ret     
       
       
check_line:
    mov     cx, 0
    
    check_line_loop:     
    cmp     cx, 0
    je      first_line
    
    cmp     cx, 1
    je      second_line
    
    cmp     cx, 2
    je      third_line  
    
    call    check_column
    ret    
        
    first_line:    
    mov     si, 0   
    jmp     do_check_line   

    second_line:    
    mov     si, 3
    jmp     do_check_line
    
    third_line:    
    mov     si, 6
    jmp     do_check_line        

    do_check_line:
    inc     cx
  
    mov     bh, 0
    mov     bl, game_pointer[si]
    mov     al, ds:[bx]
    cmp     al, "_"
    je      check_line_loop
    
    inc     si
    mov     bl, game_pointer[si]    
    cmp     al, ds:[bx]
    jne     check_line_loop 
      
    inc     si
    mov     bl, game_pointer[si]  
    cmp     al, ds:[bx]
    jne     check_line_loop
                 
                         
    mov     win_flag, 1
    ret         
       
       
       
check_column:
    mov     cx, 0
    
    check_column_loop:     
    cmp     cx, 0
    je      first_column
    
    cmp     cx, 1
    je      second_column
    
    cmp     cx, 2
    je      third_column  
    
    call    check_diagonal
    ret    
        
    first_column:    
    mov     si, 0   
    jmp     do_check_column   

    second_column:    
    mov     si, 1
    jmp     do_check_column
    
    third_column:    
    mov     si, 2
    jmp     do_check_column        

    do_check_column:
    inc     cx
  
    mov     bh, 0
    mov     bl, game_pointer[si]
    mov     al, ds:[bx]
    cmp     al, "_"
    je      check_column_loop
    
    add     si, 3
    mov     bl, game_pointer[si]    
    cmp     al, ds:[bx]
    jne     check_column_loop 
      
    add     si, 3
    mov     bl, game_pointer[si]  
    cmp     al, ds:[bx]
    jne     check_column_loop
                 
                         
    mov     win_flag, 1
    ret        


check_diagonal:
    mov     cx, 0
    
    check_diagonal_loop:     
    cmp     cx, 0
    je      first_diagonal
    
    cmp     cx, 1
    je      second_diagonal                         
    
    ret    
        
    first_diagonal:    
    mov     si, 0                
    mov     dx, 4 ;jump size
    jmp     do_check_diagonal   

    second_diagonal:    
    mov     si, 2
    mov     dx, 2
    jmp     do_check_diagonal       

    do_check_diagonal:
    inc     cx
  
    mov     bh, 0
    mov     bl, game_pointer[si]
    mov     al, ds:[bx]
    cmp     al, "_"
    je      check_diagonal_loop
    
    add     si, dx
    mov     bl, game_pointer[si]    
    cmp     al, ds:[bx]
    jne     check_diagonal_loop 
      
    add     si, dx
    mov     bl, game_pointer[si]  
    cmp     al, ds:[bx]
    jne     check_diagonal_loop
                 
                         
    mov     win_flag, 1
    ret  
           

game_over:        
    call    clear_screen   
    
    lea     dx, game_start_message 
    call    print
    
    lea     dx, new_line
    call    print                          
    
    lea     dx, game_draw
    call    print    
    
    lea     dx, new_line
    call    print

    lea     dx, game_over_message
    call    print  
    
    lea     dx, player_message
    call    print
    
    lea     dx, player
    call    print
    
    lea     dx, win_message
    call    print 

    jmp     fim
  
     
set_game_pointer:
    lea     si, game_draw
    lea     bx, game_pointer          
              
    mov     cx, 9   
    
    loop_1:
    cmp     cx, 6
    je      add_1                
    
    cmp     cx, 3
    je      add_1
    
    jmp     add_2 
    
    add_1:
    add     si, 1
    jmp     add_2     
      
    add_2:                                
    mov     ds:[bx], si 
    add     si, 2
                        
    inc     bx               
    loop    loop_1 
 
    ret  
         
       
print:      ; print dx content  
    mov     ah, 9
    int     21h   
    
    ret 
    

clear_screen:       ; get and set video mode
    mov     ah, 0fh
    int     10h   
    
    mov     ah, 0
    int     10h
    
    ret
       
    
read_keyboard:  ; read keybord and return content in ah
    mov     ah, 1       
    int     21h 
    ret 
                

            
     
    
    ret 
         
      
fim:
    jmp     fim         
      
code ends

end start


Solution 1:[1]

Correcting the initialization

Your game_pointer array consists of bytes yet the set_game_pointer routine writes words. This error does not harm the program because all references to the game_draw string happen to have their high byte zero and the win_flag that immediately follows the pointer list in memory starts at zero anyway.
The preferred solution is to define the pointer list as words and fill the list at assembly time because its initialization does not deserve runtime code.

game_pointer    dw game_draw,      game_draw + 2,  game_draw + 4
                dw game_draw + 7,  game_draw + 9,  game_draw + 11
                dw game_draw + 14, game_draw + 16, game_draw + 18
win_flag        db 0
OccupiedSquares db 0
player          db "0$" 

Improving the user input

You are neglecting to verify the user input! You assume that it will always be a digit from "1" to "9". And worse is that you don't even check if the indicated square on the playfield is still empty and thus available for modification.
In next code I suggest a solution for this:

; Read, calculate, and update draw position                   

update_draw:
  ReDo:
    call    read_keyboard      ; -> AL
    sub     al, '1'
    cmp     al, 8
    ja      ReDo               ; Invalid key
    mov     bh, 0
    mov     bl, al
    shl     bx, 1              ; Double because the pointer list contains WORDS
    mov     bx, game_pointer[bx]
    cmp     byte ptr [bx], '_'
    jne     ReDo               ; Square is not empty
    mov     al, 'x'
    cmp     player, '0'
    je      update 
    mov     al, 'o'            ; If it's not player 0, then it's surely player 1
  update:         
    mov     [bx], al 
    inc     OccupiedSquares    ; See next paragraph
    ret     

Ending the game

You say "It does not terminate in case of a draw". Well, it's even worse than that! Only if a 'win' is detected does the program end. What do you expect the players do when the playfield has no more vacant squares? That's the condition to check.
You could find out by looking at the 9 bytes that make up the grid, but much easier is to keep a count of the valid keys that the program received and if that count is equal to 9, you know that all the squares are occupied.

    call    update_draw
    call    check  
    cmp     win_flag, 1 
    je      game_over  
    cmp     OccupiedSquares, 9
    je      game_over

    xor     player, 1        ; change_player 
    jmp     main_loop

The game_over code in the end will start executing an endless loop. Why is this? The normal way to terminate the program would be to call a DOS function dedicated for that purpose:

fim:
    mov     ax, 4C00h    ; DOS.Terminate
    int     21h

Reducing the program's footprint

The check, check_line, check_column, and check_diagonal codes are too complicated. And you repeat too much of the same code!
The algorithm that you wrote in the check_diagonal routine is perfect for verifying the others too.
The difficult part here will be the pop ax instruction when a 'win' is detected. This pop removes the return address of the current call to the do_check subroutine. This in turn means that the ret that follows, will be returning to the parent of the whole check routine and that's exactly where you check your win_flag in order to decide about 'game over'.

check:
    ; 3 rows
    mov     dx, 2      ; jump size
    mov     si, 0   
    call    do_check
    mov     si, 6
    call    do_check
    mov     si, 12
    call    do_check
    ; 3 columns
    mov     dx, 6
    mov     si, 0   
    call    do_check
    mov     si, 2
    call    do_check
    mov     si, 4
    call    do_check
    ; 2 diagonals
    mov     dx, 8
    mov     si, 0   
    call    do_check
    mov     dx, 4
    mov     si, 4
    call    do_check                ; Must remain a tail-call!
    ret

  do_check:
    mov     bx, game_pointer[si]
    mov     al, [bx]
    cmp     al, "_"
    je      cont                    ; Empty square
    add     si, dx
    mov     bx, game_pointer[si]    
    cmp     al, [bx]
    jne     cont                    ; Not identical letter
    add     si, dx
    mov     bx, game_pointer[si]  
    cmp     al, [bx]
    jne     cont                    ; Not identical letter
    mov     win_flag, 1

    pop     ax                      ; Forget `call do_check`

  cont:
    ret

Solution 2:[2]

I added the line below under ;set segment registers

mov     bp,9

After ;check if game ends I added the code below to calculate draw position

;Calculate draw position
sub bp,1 
cmp bp,0
je game_drawn

Below game_over: I added game_drawn to print game draw message

game_drawn:        
call    clear_screen   

lea     dx, game_start_message 
call    print

lea     dx, new_line
call    print                          

lea     dx, game_draw
call    print    

lea     dx, new_line
call    print

lea     dx, game_draw_message
call    print  

lea     dx, player_message
call    print

jmp     fim

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sep Roland
Solution 2 Stephanie