返回控制之前,汇编程序不显示或循环

问题描述

我对汇编语言很陌生,所以这里没有介绍,但是我有这个程序应该返回一个或多个字符的反数,例如A-> Z,B-> Y等。代码可以正常工作并正确转换字符,但程序不应该在屏幕上显示它或在应有的时候循环显示它,而是返回int 21h的控制权。

.8086
.model small
.data
    
    ; Variables
    prompt_in db 0Dh,"Input: $"
    prompt_out db 0Dh,0Ah,"Output: $"


.code
   main proc
    
    ; Load Variables
    mov ax,@data
    mov ds,ax
    
    ; display Input Prompt
    mov dx,offset prompt_in
    mov ah,09h
    int 21h
    
    mov cl,00
    mov ah,01h
    
    ; Read input and push to BX until User presses Return (Enter)
    read:
        int 21h
        mov bl,al
        push bx
        inc cx
        
        cmp al,0Dh
        jz return
        
        jmp read
    
    ; display Output Prompt    
    return:
        mov dx,offset prompt_out
        mov ah,09h
        int 21h
    
    ; Calculate Inverse Letter and display      
    calc:
    
        mov ah,00
        pop bx
        
        cmp bl,0Dh
        jz calc
        
        mov al,bl
        sub al,41h
        mov cl,19h
        mov ch,2
        mul ch
        sub cl,al
        add bl,cl
        
        mov dl,bl
        int 21h ; Stops Code Here without displaying anything more
        loop calc ; Doesn't Loop Here
               
    
        mov ah,4Ch
        int 21H
    main endp
end main 

解决方法

将字符写入标准输出的DOS服务是int 21h with ah = 02h。但这不是你在做什么:

  • 仅在calc标签之后,您就加载ah而不是00的值。

  • 几行后,您有了一条02h指令。请记住,x86上的mul ch正在扩大; 8位乘法会在mul中产生16位结果,尤其是会覆盖ax。现在,在您的程序中,结果可能适合8位,因此ah再次设置为零。

当您使用ah调用int 21h时,会调用terminate program serviceah = 0的不推荐使用的前身),这就是程序此时终止的原因。

快速的解决方案是在int 21h / ah = 4Ch之前插入mov ah,02h,并删除较早的int 21h,这是错误的,也是多余的。

您可以通过注意到乘以2与向左移1相同来进一步改进程序,这是更快的并且不需要太多寄存器。如果结果溢出8位(结果的大部分丢失,而不是放入mov ah,00中),结果将完全不同,但是您无论如何都不希望处理这种情况。因此,您的ah可以简单地替换为mov ch,2 / mul ch。而且,此操作根本不会触及shl al,1

,

Nate Eldredge's answer彻底解释了为什么您的程序在显示任何输出之前就早退出的原因。希望您现在已纠正此问题。然后您会看到程序崩溃

  1. 您正在使事情复杂化。将A-Z转换为Z-A的任务很简单,不涉及乘法和移位。

    sub al,"A"
    sub al,"Z"-"A"
    neg al
    add al,"A"
    

    更短:

    sub al,"A"
    neg al
    add al,"Z"
    
  2. 该程序将永远循环并最终崩溃,因为您使用的是依赖于LOOP寄存器的CX指令,并且您的代码正在使用大的非零值重新加载此计数器寄存器值。

     mov ch,2      <-- reloads with value 512+
     ...
     loop calc      <-- jumps forever
    

这将起作用:

    pop  dx     ; First pop is the carriage return for sure!
                ; Why do you even push it in the first place?
calc:
    pop  dx
    sub  dl,"A"
    neg  dl
    add  dl,"Z"
    mov  ah,02h  ; DOS.PrintCharacter
    int  21h
    loop calc

请注意,在输入开始时,您应该将整个CX寄存器清零,而不仅仅是写入mov cl,0。依靠您使用的模拟器的特定行为(大约为零寄存器)是一个坏习惯。