程序集在打印消息时跳过第一个符号

Assembly skips first symbol while printing a message

这是我第一次用 Assembly 编写代码,所以代码可能并不完美(甚至不是很好),但这是我的工作方式,不要评判太多:)。它是为使用 DOSbox 和 Turbo Assembler/Linker/Debugger 的 Intel x86 编写的,如果有任何区别的话。任务是从用户那里获取一行并将所有大写字母转换为小写字母(保留其他所有内容)。问题是,当我一个一个地打印每个符号并在其后添加'\n'(新行)时,它工作正常,但是,我需要我的输出在一行中,所以当我尝试打印时没有' \n',它会跳过第一个符号。为什么会这样,如何修复它,以及为什么它与 '\n' 一起使用?

; Task is to get input from the user and convert any upper case letters into lower case
; and print modified line

.model small
.stack 100h

.data
    start_msg db "Enter a line:", 0Dh, 0Ah, 24h    ; "line\n", $
    out_msg db "Converted line:", 0Dh, 0Ah, 24h
    buffer db 100, ?, 100 dup(0)
.code

start:
    mov dx, @data                  ; moves data into dx
    mov ds, dx                     ; moves dx (data) into data segment

    ; prints start_msg
    mov     ah, 09h
    mov     dx, offset start_msg
    int     21h
    
    ; reads inputed line and puts it into buffer
    
    mov ah, 0Ah
    mov dx, offset buffer
    int 21h
    
    ; prints '\n' 
    mov dl, 0Ah 
    mov ah, 02h
    int 21h
    
    ; prints out_msg
    mov ah,  09h
    mov dx, offset out_msg
    int 21h
    
    ; puts pointer to the first character of inputed line in bx
    mov bx, offset buffer + 2
    loopString: 
        
        mov dl,[bx] ; reads a simbol from buffer and puts into dl
    
        cmp dl, 'A' ; if symbol is "more" than 'A', jump to ifUpper
        jae ifUpper
        
        ; prints simbol
        print:
            mov ah, 02h
            int 21h
        
        ; checks, if line ended
        inc bx ; bx++
        cmp dl, 0
        je endLoop ; ends looping
        
        ; temp part of code, puts '\n' after each printed symbol 
        ; if it is commented, skips first character from inputed line
        ; everything works if used, however I need final output to be in one line. 
        ;mov dl, 0Ah ; Uncomment me
        ;mov ah, 02h ; Uncomment me
        ;int 21h ; Uncomment me
        
        jmp loopString ; if line end was not detected, loops again 
        
    ifUpper:
        cmp dl, 'Z' ; checks if symbol is "more" than 'Z'
        ja print ; it is not upper, just prints the symbol
        add dl, 20h ; +32 (converts upper to lower)
        jmp print ; prints converted symbol
        
    endLoop:
    
    mov ah, 4ch             ; back to dos
    mov al, 0               ; error
    int 21h                
end start

Un\comment 行表示“取消注释我”以查看带和不带 '\n' 的输出。提前致谢。

cmp dl, 0
je endLoop ; ends looping

这是您的代码中有问题的部分。您应该与值 13 进行比较。我建议您阅读 DOS.BufferedInput 函数在 中的工作原理。 DOS 总是包含回车符 return 作为终止字节

  • 您已选择与零进行比较,因为您是从全零输入缓冲区开始的。然而,当 DOS return 控制您的代码时,您正在寻找的零可能甚至不再存在!如果用户在键盘上首先键入一个(非常)长的文本,然后开始退格,零就消失了。

  • 如果零仍然在正确的位置,还有第二个原因导致失败。您在打印 之后放置了零支票 ,这没有意义,因为它不是输入的一部分,是吗?现在 ASCII 代码 0 打印为 space 字符,因为零之前的字节不可避免地是一个回车 return (13),光标已经移动到行的开头和第一个字符行被删除。打印 ASCII 码 0 真的没什么神奇的。

你可以这样写。因为您确实要打印终止符 return,所以即使 'empty input' 也包含一个字节。因此 Repeat-Until 循环是可以的。

    mov  bx, offset buffer + 2
Repeat:
    mov  dl, [bx]
    cmp  dl, 'A'
    jb   print
    cmp  dl, 'Z'
    ja   print
    add  dl, 32     ; Make LCase
print:
    mov  ah, 02h
    int  21h
    inc  bx
    cmp  dl, 13
    jne  Repeat     ; Until 13 was printed
    mov  dl, 10 
    int  21h