Search code examples
assemblyemu8086

Assembly program not displaying or looping before returning control


I have this program that should return the inverse of a character or characters eg A -> Z, B -> Y etc. The code works and properly converts the character but the program doesn't display it on screen or loop when it should, instead, it returns control at 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 

Solution

  • The DOS service to write a character to standard output is int 21h with ah = 02h. But that's not what you are doing:

    • Just after the calc label you load ah with the value 00, not 02h.

    • A few lines later, you have a mul ch instruction. Remember that mul on x86 is widening; an 8-bit multiply produces a 16-bit result in ax, which in particular overwrites ah. Now in your program, the result will probably fit in 8 bits, so that ah is set to zero again.

    When you call int 21h with ah = 0, you invoke the terminate program service (a deprecated predecessor to int 21h / ah = 4Ch), which is why your program terminates at this point.

    The quick solution is to insert mov ah, 02h just before the int 21h, and remove the earlier mov ah, 00 which is wrong and also redundant.

    You can improve the program further by noticing that multiplication by 2 is the same as a left shift by 1, which is faster and doesn't involve so many registers. It won't be quite the same if the result overflows 8 bits (the high part of the result is lost, instead of being put in ah) but you don't expect to handle that case anyway. So your mov ch, 2 / mul ch can be replaced with simply shl al, 1. This moreover doesn't touch ah at all.