# Guessing game where the first player inputs a word and a hint and the second player tries to guess it

We have been given this project and I was wondering if someone could make my algorithm better using TASM and DOSBox.

This is what I have done so far:

ideal
model small
stack 10244
dataseg
msg1 db 'Enter Word: $' msg2 db 'Enter Hint:$'
msg3 db 'Enter Guess(Press . to end guess)($' wrnmsg db 'Wrong!$'
a3msg db 'Number of letters: $' a4msg db 'First two letters:$'
a5msg db 'Last letter: $' a6msg db 'Second to the last letter:$'
fail db ' Enter guess again($' secret db 21 dup('') hint db 101 dup('') guess db 21 dup('') endss db '):$'
win db 'That is correct! You win!$' lose db 'Im sorry you lose$'
cnt dw 0
codeseg
PROC compare
mov bx, 0
mov di, 0

loop1:
mov al, [secret+di]
mov ah, [guess+di]

cmp ah, al
jne no
cmp al, '$' je endi no: mov bx, 1 endi: ret ENDP PROC main mov ax, @data mov ds, ax mov es, ax lea dx, [msg1] mov ah, 9 int 21h mov bx, 0 scan: mov ah, 1 int 21h cmp al, '.' je com inc [cnt] mov [secret+bx], al inc bx jmp scan com: mov [secret+bx], '$'

mov dx, 10
mov ah, 2
int 21h

lea dx, [msg2]
mov ah, 9
int 21h

lea dx, [hint]
mov ah, 10
int 21h

mov dx, 10
mov ah, 2
int 21h

lea dx, [msg3]
mov ah, 9
int 21h

lea dx, [hint]
mov ah, 9
int 21h

lea dx, [endss]
mov ah, 9
int 21h

mov cx, 0
loop2:
inc cx
mov bx, 0
scan2:
mov ah, 1
int 21h
cmp al, '.'
je com2
mov [guess+bx], al
inc bx
jmp scan2
com2:
mov [guess+bx], '$' mov dx, 10 mov ah, 2 int 21h call compare cmp bx, 0 je right lea dx, [wrnmsg] mov ah, 9 int 21h cmp cx, 3 je three cmp cx, 4 je four cmp cx, 5 je five cmp cx, 6 je six cmp cx, 7 je fail man: mov dx, 10 mov ah, 2 int 21h lea dx, [fail] mov ah, 9 int 21h lea dx, [hint] mov ah, 9 int 21h lea dx, [endss] mov ah, 9 int 21h jmp loop2 right: lea dx [win] mov ah, 9 int 21h jmp end1 fail: lea dx, [lose] mov ah, 9 int 21h jmp end1 three: lea dx, [a3msg] mov ah, 9 int 21h lea dx, [cnt] mov ah, 2 int 21h jmp man four: lea dx, [a4msg] mov ah, 9 int 21h mov di, 0 mov si, 1 mov dh, [secret+di] mov ah, 2 int 21h mov dh, [secret+si] int 21h jmp man five: lea dx, [a5msg] mov ah, 9 int 21h mov di, [cnt] mov dl, [secret+di] mov ah, 2 int 21h jmp man six: lea dx, [a6msg] mov ah, 9 int 21h mov di, [cnt] dec di mov ah, 2 int 21h jmp man end1: mov ah, 4ch int 21h ENDP END main  What was required of us was a program that will simulate a guessing game where the first player will input the word to be guessed by the second player including a hint such as what category and other description. (Description can have a maximum of 100 characters). The second player will be given 6 chances to guess. If in the third try, the 2nd player still cannot guess the word, hints will be given for the succeeding unsuccessful guesses as cited below: • Third unsuccessful try – Number of letters will be displayed on screen • Fourth unsuccessful try – First two letters will be revealed • Fifth unsuccessful try – Last letter will be revealed • Sixth unsuccessful try- Second to the last letter will be revealed ## 1 Answer You have a bit of code duplication that could be extracted and consolidated. But first with some error fixes (at least the code did not compile on my TASM). ## Compilation issues right: lea dx [win] ; <-- missing comma between dx and [win]  fail is defined twice. First as a string (fail db ' Enter guess again($') and the second time as a label. I would rename the first one to failmsg and use it in

lea dx, [failmsg] ; <-- instead of fail before


With those fixes the code compiles now. As for the code review I would start with

## DOS functions

mov ah,09
int 21h


which is just Display string. So let's call it like that and make sure a requirement that when calling it we have dx

PROC display_string
mov ah,09
int 21h
ret
ENDP


lea dx, [a3msg]
mov ah, 9
int 21h


just do

lea dx, [a3msg]
call display_string


The same goes for other DOS functions

PROC get_char
mov ah,01
int 21h
ret
ENDP

PROC put_char
mov ah,02
int 21h
ret
ENDP


Using those instead of bare int-ing will make your code more readable. Having those it's clear what you try to do with this code

## High-level functions

mov dx, 10
call put_char


but in DOS it should be 2 characters: 13,10 so it's better to do like this:

PROC new_line
mov dx,13
call put_char
mov dx,10
call put_char
ret
ENDP


## Buffered input

I don't know why you chose buffered input as your method of entering the hint, but in case of this command the data has a specific format.

hint db 100,?,101 dup(0)


The actual string starts on index 2 and before that you have max string length, and bytes read. Also there's no $ at the end. You have to put it there. xor bx,bx mov bl, [hint+1] mov [hint+bx+2], '$'


So printing the hint should look like this:

lea dx, [hint+2]
call display_string


## Printing the count

This is obviously wrong

lea dx, [cnt]
call put_char


as in the cnt will be the number of characters not the ascii representation of that number. If we could restrict ourselves to maximum 9 characters that would be easy. Just add 48(dec) to the cnt and you get the value. Since your secret is limited to 21 characters we could go with a simple method for printing.

PROC print_number
push bx
push dx
mov bx,10
xor dx,dx
div bx
mov bx,dx
cmp ax,0
je second
mov dx,ax
call put_char
second:
mov dx,bx
call put_char
pop dx
pop bx
ret
ENDP


What we do here is just divide the number in ax by 10 and print the values stored in ax (result - only if >0) and the dx - reminder.

## Printing the hints

On fourth attempt you are putting the character to be printed in dh. It should be in dl. You don't also have to use different registers so just:

mov dl, [secret]
call put_char
mov dl, [secret+1]
call put_char


To print the last char you need to keep in might that the last is '$' so just one before the last mov di, [cnt] mov dl, [secret+di-1] call put_char  The same issue when you try to print the 6th hint. Also that you did not retrieve the char at all. mov di, [cnt] sub di,2 mov dl, [secret+di-1] call put_char  ## Compare Your compare method is kinda strange. You do not iterate at all and you don't use any opcodes that would do that for you. It works only when you have one-char secret. Not so cool :) I would write it like this PROC compare push cx lea si, [secret] lea di, [guess] rep cmpsb pop cx ret ENDP  and change how to check for equality call compare je right  ## Other things that might be good to fix You have the same loop for scanning the characters. Why not enclose it as another proc and just use in those two places? You can make assumption that for example di will point to the buffer that has to be filled. Also in this scanning you could handle for example backspace character and delete the characters. PROC read_input push cx mov bx, 0 mov cx, 0 scan: call get_char cmp al, '.' je com inc cx mov [di+bx], al inc bx jmp scan com: mov [di+bx], '$'
mov ax,cx
pop cx
ret
ENDP


And use it

lea di,[secret]
mov [cnt],ax


and similarly in the second place.

Also some checks for input length might be good to do.

You should also comment more your code. I strongly believe the code should be self documented but not on such low level as asm :) So comment more.

GIST

• cmp reg, 0 => test reg, reg. mov reg, 0 => xor reg, reg. Your proposed compare method is invalid, since you don't actually set cx. Furthermore, you should be using either repe/repz or repne/repnz with cmpsb, not just rep. You also don't need lea there; a simple mov would work just fine to get the address of the secret and guess variables. And you probably shouldn't be assuming the state of the direction flag; procedures that use string instructions should explicitly CLD or STD. – Cody Gray May 26 '17 at 12:21
• in general you are true with the cmp/test, and mov/xor but those are only shorter opcodes and I wouldn't go with that if I don't need to reduce the size as much as possible. As for the compare I'm pretty sure I've tested it and worked so maybe I've missed something when writing the answer - need to check the code again; as for the direction flag - agreed. Should be explicitly set. – Paweł Łukasik May 26 '17 at 20:46
• They are shorter opcodes, which makes them faster. There is never a disadvantage. It should just be the way you write the code in the first place, like standard idioms. Your assembler probably converts the simple rep prefix into repe, then. Some of them do this, trying to be helpful. – Cody Gray May 28 '17 at 8:53