Reputation: 23
I'm new to assembly language and I'm struggling to find out what is going on with this program I am writing . The goal of the program is for the user to enter a number between 0 and 9 . It then checks if this number is a multiple of 3 , if an incorrect character (i.e Z ) is entered it should simply put '**' next to the character and ask again for the user to enter a number. If a number is entered it is then converted from a ASCII character to a number and then divided .
Now the problem is when I enter a correct character(0-9) first the answer comes back correctly . Lets say I enter 3 it will say its a multiple , if I enter 4 first it will say its not a multiple which is correct . But as soon as I enter a incorrect character and it starts looping it doesn't give me the correct answer once I enter in a correct character . Please look at this code and see if anyone can help me .
HERE IS THE CODE:
bits 16
org 0x100
jmp main
name db 'Assembly Program Divider','$',0dh,0ah
prompt db 'Please enter a number (0 - 9): ','$',0dh,0ah
invalid db 'Invalid Character','$',0dh,0ah
is db 'Number is a multiple of 3','$',0dh,0ah
isnot db 'Number is not a multiple of 3','$',0dh,0ah
asteriks db '**','$',0dh,0ah
cr_lf:
db 0dh,0ah,'$' ; carriage return and line feed
;Scroll and clear the screen and also change background and text color
clr_scr:
mov ax,0600h ; Scroll the entire screen
mov bh,1Eh ; Colour: white to blue
mov cx,00 ; From top left corner
mov dx,184Fh ; To lower right corner
int 10h ; BIOS interrupt
nop
ret
;Set the cursor position on the screen
set_csr:
mov ah,02 ; Set cursor
mov bh,00 ; Screen number 0
mov dx,0a00h ; row 10, column 0
int 10h ; BIOS interrupt
ret
;Display a message on the terminal
disp_str:
mov ah,09 ; Display message
int 21h ; DOS interrupt
ret
;Read a character from terminal
read_chr:
mov dx,cr_lf ; address of line feed
call disp_str ; display line feed
mov dx,prompt ; address of prompt
call disp_str ; display prompt
mov ah,01 ; get character from keyboard
int 21h ; DOS system call
cmp al,39h
jg diss_invalid
cmp al,30h
jl diss_invalid
ret
;Display a message for an invalid character entered
diss_invalid:
mov dx,asteriks ;address of asteriks to display next to character
call disp_str ; display asteriks
call read_chr ; run the read character again untill correct character is entered
;Display message for when the number is a multiple of 3
diss_is:
mov dx,cr_lf ; address of line feed
call disp_str ; display line feed
mov dx,is
mov ah,09 ; Display message
int 21h ; DOS interrupt
int 20h ; Terminate Program
;Display message for when the number is not a multiple of 3
diss_isnot:
mov dx,cr_lf ; address of line feed
call disp_str ; display line feed
mov dx,isnot
mov ah,09 ; Display message
int 21h ; DOS interrupt
int 20h ; Terminate program
str_to_num:
mov dx, 0
sub al,0 ; ASCII value converted to numeric value
mov ah,0 ; AX = numeric value of digit
add dx,dx ; DX = 2 * original DX
add ax,dx ; AX = 2 * original DX + digit
add dx,dx ; DX = 4 * original DX
add dx,dx ; DX = 8 * original DX
add dx,ax ; DX = 10 * original DX + digit
ret
divide:
mov ah,0 ; clear AH
mov dl,03h ; Divisor 3 in DL
idiv dl ; integer division , remainder stored in AH
cmp ah,0 ; compare the remainder to 0
je diss_is ; if the reminder = 0 go to display is
cmp ah,0 ; compare the remainder to 0
jg diss_isnot ; if the reminder is not 0 go to display isnot
;Main program to be executed
main:
call clr_scr ; clear screen
call set_csr ; set cursor
mov dx,name ; address of name and student number
call disp_str ; display name and student number
call read_chr ; read character
call str_to_num ; convert the character received to a number
call divide ; divide the converted number by 3 and check if it has a remainder
int 20h ; Terminate the program if all is successfull
Thanks in advance !!
Here is examples of what happens:
Please enter a number (0 - 9)j**
Please enter a number (0 - 9)k**
Please enter a number (0 - 9)7
Number is a multiple of 3
Please enter a number (0 - 9)j**
Please enter a number (0 - 9)k**
Please enter a number (0 - 9)8
Number is a multiple of 3
Please enter a number (0 - 9)j**
Please enter a number (0 - 9)k**
Please enter a number (0 - 9)3
Number is a multiple of 3
Please enter a number (0 - 9)7
Number is not a multiple of 3
Please enter a number (0 - 9)3
Number is a multiple of 3
Upvotes: 2
Views: 1130
Reputation: 16626
I will add to this answer based on your responses in comments...
First about the:
divide:
mov ah,0
mov dl,03h
idiv dl
cmp ah,0
je diss_is
cmp ah,0
jg diss_isnot
it divides the number in ax (dividend) with the divisor in dl (where I moved the hex number 3 into) the remainder is then stored in ah . I then compare ah with 0 , when it is 0 there is no remainder and it must be a multiple of 3 .
Quite good, but let's nitpick, because CPU does too (well, it just follows your instructions literally, not caring about nitpicking, but it feels sometimes like it is).
"the hex number 3 into" - the number 3 is number 3. We have many possible forms how to express it, from common decimal format 3
used by many humans, trough similar hexadecimal format 03h
(in some assemblers also C-like 0x03
works, or even $03
), binary 0b11
, octal 0q03
or 03
, trough less numeric ways, like Roman numbers III
, or just some expression like ***
(count of stars). In the CPU I prefer to imagine it as 8 (16/32/..) wires, each having either current flowing trough it, or not. Each wire then represents single bit, composing the 8/16/32/.. bit number.
So it doesn't matter how you write that 3, if it's hexadecimal form 03h
or decimal 3
, it will set bits in dl
as 00000011
.
You can easily think about that "hexa number 3" as 3, there's no benefit to have it in hexa in this particular case, actually as the task description was about division by 3, I would rather stick to decimal form even in the source code. You will get the feel, where hexadecimal form is simpler, over time.
You then compare the remainder with zero, when equal, you jump to diss_is
. That's completely correct.
But that's not all of the code, and it's not covering all possible cases. When the remainder is not equal to zero (it's 1 or 2), what will happen? After cmp ah,0
the flags will be set up, in the same way, as after sub ah,0
. Both 1-0 and 2-0 will set the interesting flags in the same way: ZF=0, CF=0, SF=0, OF=0.
So je diss_is
will not branch, and the execution will continue on the next cmp ah,0
. But did the je
change some flags? It didn't. So it's not needed to do the cmp
again, you already have the result of it in the flags from the first one.
Then you do jg diss_isnot
, which will branch for both 1
and 2
, but as you covered the "0 remainder" case with je
, you can just jmp diss_isnot
directly without any condition. Or move the code from diss_isnot
under the je
.
If there would be some additional code changing flags ahead of jg
, making it possible to not branch, the code would re-enter the main
by accident! Can't happen in your case, but don't write code in this way, use simpy jmp
to avoid any confusion.
Actually you do call divide
in the main
, but you will never return from it, because both branches have their own int 20h
. You could shorten your code a bit by sharing removing duplicities in code, like calling the cr_lf before test (just make sure you store al
with number somewhere, stack should work well), or even displaying the result by the same code, just adjusting the dx,<result_message_offset>
in the branch, and then using the same code to end divide
. And ret
from it, so exit is only in main
.
... end of part 1 ...
Now look at that sub al,0
ASCII conversion.
And finally check your code flow for non number char, how it will branch, and what will happen on stack. Keep commenting back, what you think the code does.
Upvotes: 1
Reputation: 2688
So this could be wrong because I didn't test it but I’m thinking at the end of diss_invalid
you call read_chr
right? And when it returns it returns to the next instruction after it was called back under diss_invalid
.... which isn’t what you meant right? So instead of calling read_chr
, I would put a label in main after call disp_str ; display name and student number
that you can jump to from diss_invalid
. But that is just a temporary solution. A more permanent one would be to start commenting EVERYTHING and to get out a piece of paper and draw out what you think should happen, then check if the code you wrote does that. Try to modularize everything. Should read char really be sanitizing input or should that be a separate function? etc...
Upvotes: 1