我是汇编语言的新手,我正在努力找出我正在编写的这个程序正在发生什么。该程序的目标是让用户输入0到9之间的数字。然后它检查这个数字是否是3的倍数,如果输入了错误的字符(即Z),它应该简单地放入' **'在角色旁边再次询问用户输入数字。如果输入一个数字,则将其从ASCII字符转换为数字,然后进行分割。
现在问题是当我输入正确的字符(0-9)时,答案才会正确回复。让我们说我输入3它会说它是一个倍数,如果我先输入4它会说它不是一个正确的倍数。但是一旦我输入一个不正确的字符并开始循环,一旦我输入正确的字符,它就不会给我正确的答案。请查看此代码,看看是否有人可以帮助我。
这是代码:
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
提前致谢!!
以下是发生的事情的例子:
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
答案 0 :(得分:1)
所以这可能是错的,因为我没有对它进行测试,但是我在diss_invalid
结束时你正在考虑read_chr
对吗?当它返回时,它返回到diss_invalid
下被回调后的下一条指令....这不是你的意思吗?因此,我不会调用read_chr
,而是在call disp_str ; display name and student number
后面的主要标签中添加一个标签,您可以从diss_invalid
跳转到{{1}}。但这只是一个临时解决方案。一个更永久的方法是开始评论一切并拿出一张纸,然后画出你认为应该发生的事情,然后检查你写的代码是否会这样做。尝试模块化一切。应该读取char真的是消毒输入还是应该是一个单独的功能?等...
答案 1 :(得分:1)
我会根据你在评论中的回答添加这个答案......
首先关于:
divide:
mov ah,0
mov dl,03h
idiv dl
cmp ah,0
je diss_is
cmp ah,0
jg diss_isnot
它将ax(dividend)中的数字除以dl中的除数(我将十六进制数字3移入其中),然后将余数存储在ah中。然后我将0与0进行比较,当它为0时,没有余数,它必须是3的倍数。
相当不错,但是让我们挑剔,因为CPU也是如此(好吧,它只是遵循你的指示字面意思,不关心挑剔,但感觉有时会像它一样)。
"十六进制数字3进入" - 数字3是数字3.我们有许多可能的表单如何表达它,使用常见的十进制格式3
许多人通过类似的十六进制格式03h
(在某些汇编程序中也类似于C 0x03
工作,甚至$03
),二进制0b11
,八进制0q03
或03
,通过较少的数字方式,如罗马数字III
,或只是某些表达式,如***
(星数)。在CPU中,我更喜欢把它想象成8(16/32 / ...)线,每个线都有电流流过或不流动。然后每根线代表单个位,组成8/16/32 / ..位数。
因此,无论你如何编写3,如果它是十六进制形式03h
或十进制3
,它都会将dl
中的位设置为{ {1}}。
您可以轻松地思考这个问题" hexa number 3"作为3,在这种特殊情况下,它在hexa中没有任何好处,实际上因为任务描述是关于除以3,我宁愿坚持使用十进制形式,即使在源代码中也是如此。随着时间的推移,您将获得十六进制形式更简单的感觉。
然后将余数与零进行比较,当相等时,跳转到00000011
。这完全正确。
但并非所有代码,并且它并未涵盖所有可能的情况。当余数不等于零(它的1或2)时,会发生什么?在diss_is
之后,将以与cmp ah,0
之后相同的方式设置标志。 1-0和2-0都将以相同的方式设置有趣的标志:ZF = 0,CF = 0,SF = 0,OF = 0。
所以sub ah,0
不会分支,执行将在下一个je diss_is
继续。但是cmp ah,0
改变了一些标志吗?它没有。因此,不需要再次执行je
,您已经在第一个标记中得到了它的结果。
然后你会cmp
jg diss_isnot
,它会为1
和2
分支,但是当你覆盖" 0余数"使用je
的情况,您可以直接jmp diss_isnot
直接无任何条件。或者从diss_isnot
下的je
移动代码。
如果在jg
之前有一些额外的代码更改标记,使得可以不分支,代码将意外重新输入main
!在你的情况下不可能发生,但不要以这种方式编写代码,使用简单的jmp
来避免混淆。
实际上你在call divide
中执行了 main
,但是你永远不会从中返回,因为两个分支都有自己的int 20h
。您可以通过共享删除代码中的重复项来缩短代码,例如在测试之前调用cr_lf(只需确保将al
存储在某处,堆栈应该正常工作),或者甚至通过相同的代码显示结果,只需调整分支中的dx,<result_message_offset>
,然后使用相同的代码结束divide
。并且ret
来自它,因此退出仅在main
。
......第1部分结束......
现在看看sub al,0
ASCII转换。
最后检查你的代码流是否有非数字char,它将如何分支以及堆栈上会发生什么。继续评论,你认为代码的作用。