汇编语言循环上的答案不正确(程序集8086)

时间:2016-09-21 22:13:47

标签: assembly x86

我是汇编语言的新手,我正在努力找出我正在编写的这个程序正在发生什么。该程序的目标是让用户输入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

2 个答案:

答案 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,八进制0q0303,通过较少的数字方式,如罗马数字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,它会为12分支,但是当你覆盖" 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,它将如何分支以及堆栈上会发生什么。继续评论,你认为代码的作用。