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

Assembly program not displaying or looping before returning control

我有这个程序应该 return 一个或多个字符的反转,例如 A -> Z,B -> Y 等。代码可以正常转换字符,但程序不显示它应该出现在屏幕上或循环播放,相反,它 return 控制在 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 标签之后,您用值 00 加载 ah,而不是 02h

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

当您使用 ah = 0 调用 int 21h 时,您会调用 terminate program serviceint 21h / ah = 4Ch 的已弃用前身),这就是您的程序在此时终止的原因。

快速的解决方案是在 int 21h 之前插入 mov ah, 02h,并删除前面的 mov ah, 00,这是错误的,也是多余的。

您可以进一步改进程序,注意乘以 2 与左移 1 相同,后者速度更快且不涉及那么多寄存器。如果结果溢出 8 位(结果的高位部分丢失,而不是放在 ah 中),情况就不太一样了,但无论如何您都不希望处理这种情况。所以你的 mov ch, 2 / mul ch 可以简单地替换为 shl al, 1。此外,这根本没有触及 ah

彻底解释了为什么您的程序甚至在显示任何输出之前就提前退出。我希望你现在已经纠正了这个问题。然后你会看到程序崩溃!

  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. 程序将永远循环并最终崩溃,因为您正在使用依赖于 CX 寄存器的 LOOP 指令并且您的代码正在使用大 non-zero 值。

     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。依赖您正在使用的模拟器的特定行为(关于归零寄存器)是一个坏习惯。