首页 > 解决方案 > 装配体在打印消息时跳过第一个符号

问题描述

这是我第一次用Assembly写代码,所以代码可能并不完美(甚至不是很好),但这是我的工作方式,不要过多判断:)。它是使用 DOSbox 和 Turbo Assembler/Linker/Debugger 为 Intel x86 编写的,如果这有什么不同的话。任务是从用户那里获取一行并将所有大写字母转换为小写字母(保持其他所有内容不变)。问题是,当我一个接一个地打印每个符号并在其后添加 '\n'(new line) 时,它工作正常,但是,我需要我的输出在一行中,所以当我尝试在没有 ' 的情况下打印时\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' 的输出。提前致谢。

标签: assemblyinputx86output

解决方案


cmp dl, 0
je endLoop ; ends looping

这是您的代码有问题的部分。您应该改为与值13进行比较。我建议您在此 Q/A中了解 DOS.BufferedInput 函数的工作原理。DOS 总是将回车作为终止字节

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

  • 如果零仍然在正确的位置,那么它失败的第二个原因。您在打印后放置了零检查,这没有意义,因为它不是输入的一部分,是吗?现在 ASCII 代码 0 打印为空格字符,并且因为零之前的字节不可避免地是回车符 (13),所以光标已经移动到行首,并且行上的第一个字符被删除。打印 ASCII 码 0 真的没有什么神奇之处。

这就是你可以写它的方式。因为您确实想打印终止的回车,所以即使是“空输入”也包含一个字节。因此,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

推荐阅读