2

I'm trying to make a guessing game in NASM assembly using a kernel. The boot loader just points to the kernel file. The kernel file stores everything. All the functions and variables for the game; while Ive gotten as far to print instructions for the game out. The problem arises; when I use an if statement. It prints out the winning message whether it's the correct number or not. I want the kernel to print the correct winning message, when the correct number is chosen. My code for the kernel is below ....

 org   0
 bits  16

_start:
 push cs
 pop  ds
 mov  si, msg
 jmp  BeginLoop

PrintLoop:
 mov  ah, 0x0E
 int  0x10

BeginLoop:
  mov  al, [si]
  inc  si
  or al, al
  jz GameLoop
  test al, al
  jnz  PrintLoop


GameLoop:
  xor ax, ax
  int 16h
  mov ah, 0x0E
  int  0x10
  cmp ax, 0x5
  jne GameWon

GameWon:
 mov si, won
 mov  al, [si]
 inc  si
 or al, al
 jz GameLoop
 test al, al
 jnz  PrintLoop


 cli
 hlt
 jmp  $-2



 msg db   'Welcome To My Guessing Game: ', 10, 13, 'Pick A Number Between            1 - 10 ', 10, 13, 0
 won db   'You Guessed The Correct Number!', 10, 13, 0
 loss db  'You Guessed The Incorrect Number!' 10, 13

 TIMES 512 - ($ - $$) db 0

Updated code that works

org 0
bits 16

_.start:
push cs
pop  ds
mov  si, msg
jmp Print

GameLoop:
mov ah, 00h
int 1ah
mov  ax, dx
xor  dx, dx
mov  cx, 10
div  cx
add  dl, '0'
mov  ah, 0x00
int  16h
mov  ah, 0x0E
int  0x10

GameCondition:
mov  si, won
cmp  al, dl
je   GameWon
jmp  GameLost

GameLost:
mov  si, loss
jmp Print

GameWon:
jmp Print

Print:
jmp  BeginLoop

PrintLoop:
mov  ah, 0x0E
int  0x10

BeginLoop:
mov  al, [si]
inc  si
or al, al
jz GameLoop
test al, al
jnz  PrintLoop
ret

; -------------------
msg db   'Welcome To My Guessing Game: ', 10, 13, 'Pick A Number Between 0 - 9 ', 10, 13, 0
won db   'You Guessed The Correct Number!', 10, 13, 0
loss db  'You Guessed The Incorrect Number!', 10, 13,  0

times 512 - ($-$$) db 0
  • When `ax` is not `5`, instruction `jne GameWon` jumps to `GameWon`, otherwise it falls through to `GameWon`. You seem to have omitted the paragraph `GameLoss:`. – vitsoft Oct 15 '22 at 07:00
  • What about `cmp al,dl` `je GameWon` `GameWon:` ? The AL register no longer contains the inputted character because the random number generation destroyed AL. And once again you let the code fall through to where you jump: that's always going to say "you won". You got answers from **2** users. At least incorporate some of the good code we wrote for you... – Sep Roland Oct 16 '22 at 21:13

2 Answers2

3
  cmp ax, 0x5
  jne GameWon

GameWon:

In code that branches on the result of a comparison, if the condition is true then the jump is taken, but if the condition is not true then the execution just continues on the following lines. We call this 'falling through'. In your program you will always arrive at GameWon.

  or al, al
  jz GameLoop
  test al, al
  jnz  PrintLoop

GameLoop:

And this is another example of not understanding the priciple of code that falls through. When the test al, al instruction determines that AL is 0, the PrintLoop will not continue and the execution will fall through into your GameLoop. Therefore it is totally redundant that you would check for an empty AL with those two lines that you added (or al, al jz GameLoop).

GameLoop:
  xor ax, ax
  int 16h
  mov ah, 0x0E
  int  0x10
  cmp ax, 0x5
  jne GameWon

The game loop waits for a key, echoes it to the screen, and then wants to compare the key with the value 0x5.
BIOS gave you the key's ASCII code in the AL register and the key's scancode in the AH register. There are 3 reasons why the cmp ax, 0x5 instruction (comparing the full 16 bits) is not going to work:

  • Scancodes start at 1, never 0
  • You need to compare ASCII codes, for 5 that is 53
  • The echoing via the Teletype destroys AH

Your program outputs several messages. You cannot continue to use the PrintLoop that I wrote in a previous answer. You need to turn this code into a subroutine that you can call multiple times:

_start:
  push cs
  pop  ds
GameLoop:
  mov  si, msg
  call Print
  mov  ah, 0x00
  int  16h         ; -> AX
  mov  ah, 0x0E
  int  0x10
  mov  si, won
  cmp  al, 0x35    ; eg. "5"
  je   GameWon
  mov  si, loss
 GameWon:
  call Print
  jmp  GameLoop    ; Truly a loop

; --------------------
; IN (si) OUT (si) MOD (ax)
Print:
  jmp  BeginLoop
 PrintLoop:
  mov  ah, 0x0E
  int  0x10
 BeginLoop:
  mov  al, [si]
  inc  si
  test al, al
  jnz  PrintLoop
  ret
; -------------------
 msg db   'Welcome To My Guessing Game: ', 10, 13, 'Pick A Number Between 
0 - 9 ', 10, 13, 0
 won db   'You Guessed The Correct Number!', 10, 13, 0
 loss db  'You Guessed The Incorrect Number!' 10, 13, 0

Tip: better ask for a single-digit number in the range 0 to 9. The number 10 is not a good choice since it doesn't have a single ASCII code.


Generate:
xor ax, ax
mov ax, 57
div ax, 3 
inc ax

Your latest edit shows that you also want to implement a random number generator. Get a grasp for it in 8086 random number generator (not just using the system time)?.
The div ax, 3 line is not a valid instruction! And the xor ax, ax needs to become xor dx, dx instead.

Next is a correct dividing operation, although of course (57/3)+1 is not going to be a surprise:

Generate:
xor dx, dx
mov ax, 57
mov cx, 3
div cx 
inc ax
ecm
  • 2,583
  • 4
  • 21
  • 29
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
1

There are a couple of things wrong. First is, as @vitsoft stated earlier, that you don't have a GameLoss condition. You are checking if something is 5 and basically ignoring the condition.

GameLoop:
  xor ax, ax
  int 16h
  mov ah, 0x0E
  int  0x10
  cmp ax, 0x5
  jne GameWon

GameWon:
 mov si, won
 mov  al, [si]
 inc  si
 or al, al
 jz GameLoop
 test al, al
 jnz  PrintLoop

To fix that the above code can be:

GameLoop:
  xor ax, ax
  int 16h
  mov ah, 0x0E
  int  0x10
  cmp ax, 0x5 ; I am assuming that the player wins if the input is 0x5
  je GameWon

GameLost:
  ; print some error message or a message stating they lost
  ; and etc
  jmp endGame

GameWon:
 mov si, won
 mov  al, [si]
 inc  si
 or al, al
 jz GameLoop
 test al, al
 jnz  PrintLoop

endGame:
 cli
 hlt
 jmp  $-2

Also there is this thing

mov ah, 0x0E
int  0x10
cmp ax, 0x5 ; I am assuming that the player wins if the input is 0x5

You are moving 0x0E to ah and then expecting ax to have a value of 0x5. This is not possible since as far as I know int 0x10 doesn't modify ah. Therefore the code should be:

mov ah, 0x0E
int  0x10
cmp al, 0x5 ; I am assuming that you want to check for the ASCII character
            ; Also are you sure that you should compare it with 0x5 and not '5'?

Here is a simple guessing game code, I haven't tried this per se, try it and adapt it to yourself:


[BITS 16]

global _start

_start:
    mov si, welcomeMsg ; print welcome message
    call printstr

    xor ax, ax ; get input
    int 0x16
    mov ah, 0x0e ; print the character
    int 0x10
    
    sub al, '0' ; convert letter to number
    cmp al, 5 ; compare the inputted value to some number
    je GameWon ; if the inputted value is equal to the number that we are looking for
               ; go to game won
    
GameLost:
    mov si, lossMsg
    call printstr
    jmp EndGame

GameWon:
    mov si, wonMsg
    call printstr

EndGame: 

    jmp $

;
; Input: si: pointer to string to print
;

printstr:
    push ax
    push si
    mov ah, 0x0e

printstrloop:
    mov al, byte [si]
    cmp al, 0
    je endprintstr
    int 0x10
    inc si
    jmp printstrloop

endprintstr:
    pop si
    pop ax
    ret

welcomeMsg: db "Welcome To My Guessing Game: ", 10, 13, "Pick A Number Between 1 - 10 ", 10, 13, 0
wonMsg: db "You Guessed The Correct Number!", 10, 13, 0
lossMsg: db "You Guessed The Incorrect Number!" 10, 13

times 512 - ($-$$) db 0x00

Edit:

This code is wrong:

add  dl, '0'

cmp al,dl
je GameWon
GameWon:
mov si, WonMsg
jmp PrintLoop
jmp EndGame

cmp al,dl
jne GameLost
GameLost:
mov si, lossMsg
jmp PrintLoop
jmp EndGame

This doesn't even make sense:

cmp al,dl
je GameWon
GameWon:

you are comparing two values and if they are equal executing GameWon. If they are not equal you are not doing anything and your code continues to execute and reaches GameWon anyways. I have said this problem several times throughout this post, it is actually the second sentence of the post. To fix the code it should be:

add  dl, '0'

cmp al,dl
jne GameLost

GameWon:
mov si, WonMsg
jmp PrintLoop
jmp EndGame

GameLost:
mov si, lossMsg
jmp PrintLoop
jmp EndGame

Now you are checking if al and dl are the same. Then if they are not you are jumping to GameLost, if they are equal you automatically execute GameWon.

This combined with @SepRoland's comment makes your final code like this:

 org   0
 bits  16

_start:
push cs
pop  ds
mov si, WelcomeMsg
jmp  BeginLoop

PrintLoop:
mov  ah, 0x0E
int  0x10

BeginLoop:
mov  al, [si]
inc  si
or al, al
jz GamePlay
test al, al
jnz  PrintLoop


GamePlay:
xor ax, ax
int 16h
push ax
mov ah, 0x0E
int  0x10
mov ah, 00h
int 1ah
mov  ax, dx
xor  dx, dx
mov  cx, 10
div  cx
add  dl, '0'
pop ax
cmp al,dl
jne GameLost

GameWon:
mov si, WonMsg
jmp PrintLoop
jmp EndGame

cmp al,dl
jne GameLost

GameLost:
mov si, lossMsg
jmp PrintLoop
jmp EndGame


EndGame:
cli
hlt
jmp  $-2


WelcomeMsg db   'Welcome To My Guessing Game: ', 10, 13, 'Pick A Number Between 1 - 10 ', 10, 13, 0
 WonMsg db   'You Guessed The Correct Number!', 10, 13, 0
 lossMsg db  'You Guessed The Incorrect Number!', 10, 13, 0

 TIMES 512 - ($ - $$) db 0x00