【问题标题】:How output string char by char in tasm?如何在 tasm 中逐个字符输出字符串?
【发布时间】:2022-10-23 22:00:30
【问题描述】:

我为 DOSBOX 编写程序(使用 tasm)。我需要将输入的字符串逐行输出为字符。我想出了如何输入字符串,但我错了 output

2个问题

  1. 不知道怎么弄长度的字符串,所以有太多的空行。
  2. 每行没有一个字符在输出中。

    我的代码:

    .model small
    .data
        message db   'String: $'
            string db 10 dup(' '), '$' 
    .stack 256h
    .code
    main:
        mov ax, @data
        mov ds, ax
    
            lea dx, message ; load message to dx
        mov ah, 09h ; output message
            int 21h
    
            xor dx, dx
            lea dx, string ; input string
        mov ah, 0Ah
        int 21h
    
            ; crlf
            mov dl, 10
            mov ah, 02h
            int 21h
            mov dl, 13
            mov ah, 02h
            int 21h
            
            ; output string char by char 
            mov si, 0
            mov cx, 10 ; a number of loops, but how to get the length of the string?
            output:
                  lea dx, string[si]
                  mov ah, 09h
                  int 21h
    
                  mov dl, 10
                  mov ah, 02h
                  int 21h
                  mov dl, 13
                  mov ah, 02h
                  int 21h
                  
                  inc si
            loop output
    
        mov ah,4ch
        int 21h
    end main
    
    

【问题讨论】:

    标签: string assembly output dosbox


    【解决方案1】:

    以下是如何按字符打印字符串:

    声明字符串,例如:

    str: db "Hello, world!", 0
    

    然后,要获取字符串的长度,您应该在字符串末尾搜索空终止符。无论哪种方式,代码都可能类似于:

    ;
    ; output string char by char
    ; input: pointer to string in si
    ;
    printstr:
        mov ah, 0x02 ; get 0x02 inside ah to use with int 21h
    printstrloop:
        cmp byte[si], 0 ; check if we are at the end of string
        je printstrend ; end if it is
        mov dl, byte[si] ; get the char inside dl to print it
        int 0x21 ; output
    
        mov dl, 0x0d ; newline character CR
        int 0x21 ; do newline
    
        mov dl, 0x0a ; newline character Lf
        int 0x21 ; do newline
    
        inc si ; increase the pointer
        jmp printstrloop ; loop
    
    printstrend:
        ret
    

    然后你可以像这样输入:

    为字符串预留空间

    length: resb 1
    actualLength: resb 1
    str: resb 255 ; I don't know TASM syntax. This is in NASM
    

    获取输入

    mov byte[length], 0xFF
    mov ah, 0x0a
    mov dx, length
    int 0x21
    

    然后在str 处有一个以CR 结尾的字符串,在actualLength 处有一个长度。然后您可以将printstrcmp byte[si], 0x0d 一起使用,而不是空终止符

    【讨论】:

    • 他的一个大问题是他错误地使用了int 21h/ah=0ah(从输入中读取字符串)并且他拥有的缓冲区没有考虑到要输入的字符串的长度是第一个字节,实际上是字符数读取在第二个字节中。不保证该字符串以零结尾,并且可能在末尾有回车符。我通常只使用从调用返回的长度字节,并使用它用 0(NUL 字符)替换回车,或者如果稍后使用 DOS 写入字符串,则使用 $
    • 您应该在每个字符后写一个 CR LF(13 10 或 0xD 0xA),而不仅仅是 LF。在 DOS 终端上,只有 LF 通常不返回回车。
    • @ecm 修复了它,感谢您的输入。
    • 字符串前有 2 个字节。第一个字节是要读取的最大数量,然后在读取字符串时用字符串的长度填充后面的字节(减去回车符)。缓冲区本身由回车终止。关于这个问题还有其他问题有很好的答案。一个例子:stackoverflow.com/a/56622882/3857942
    • @ÖzgürGüzeldereli The buffer format for int 21h function 0Ah 是一个字节的缓冲区长度(X),一个字节的实际使用长度(Y),然后是 X 个字节的缓冲区空间。您收到的实际数据存储在从ds:dx 加 2 开始。它总是由 CR 终止,但如果您愿意,可以使用 Y 指示的长度。 (Y 总是小于 X。)
    【解决方案2】:

    另一个答案提供了一个基于字符串终止符(如 0 或 13)的工作解决方案。
    这个答案选择使用字符串长度,因为它已经从 DOS 中获得,并且更符合 OP 的要求。


    .data
      message db   'String: $'
      string db 10 dup(' '), '$' 
    .stack 256h
    

    “我想出了如何输入字符串,......”不是真的!
    细绳行应该定义 DOS.BufferedInput 函数 0Ah 的输入结构。 DOS 期望在第一个字节中找到存储长度,并在第二个字节中返回实际输入的长度。 How buffered input works 有详细信息。
    你所写的细绳转换为一个 10 字节的内存区域,完全由数字 32(ASCII 的 ' ')填充,并且乐观地后面跟着一个美元字符(链接的帖子解释了为什么这不是一个好主意)。因为第一个字节是 32,它将允许 DOS 合法地使用接下来的 34 个字节用于输入目的。你有一个严重的缓冲区溢出!
    如果要允许用户输入 10 个字符,那么正确的定义是 string db 11, 0, 11 dup(0)


    2个问题

    1. 不知道怎么弄长度的字符串,所以有太多的空行。
    2. 每行没有一个字符在输出中。
      1. DOS 已经在第二个字节中为您提供了字符串的长度细绳输入结构。只需获取它:xor cx, cxmov cl, string[1]
      2. 你的代码(lea dx, string[si]mov ah, 09hint 21h使用输出字符串的 DOS 函数 (09h)。你期待什么?改用单字符输出函数:mov dl, string[si]mov ah, 02hint 21h

        您的代码(使用 cmets)

         lea  dx, message
         mov  ah, 09h
         int  21h
         lea  dx, string
         mov  ah, 0Ah
         int  21h
         mov  dl, 10             ; (*)
         mov  ah, 02h
         int  21h
         ; output string char by char 
         mov  si, 2              ; Characters start at offset 2
         xor  cx, cx
         mov  cl, string[1]      ; Count of characters
        output:
         mov  dl, string[si]     ; Fetch one character
         mov  ah, 02h
         int  21h                ; Print one character
         mov  dl, 13
         mov  ah, 02h
         int  21h                ; Print carriage return
         mov  dl, 10
         mov  ah, 02h
         int  21h                ; Print linefeed
         inc  si                 ; Move to next character
         loop output             ; Repeat for all characters
        

        (*) 在 DOS.BufferedInput 函数 0Ah 结束时,光标将位于当前行的第一列。您不需要输出回车 (13)。只需一个换行符(10)就可以了。


        我的代码

        以上输出循环确实3每次迭代的系统调用。如果我们将这个数字减少到1, 循环可以快 10% 左右运行1.
        在每次迭代中,我们将当前字符复制到以 $ 结尾的字符串,其中包含回车符和换行符字节。 DOS.PrintString 函数 09h 的单次调用然后进行输出。

        1只要屏幕不必滚动就可以,因为屏幕滚动比较慢。

        .data
        message db 'String: $'
        string  db 11, 0, 11 dup(0) 
        TheChar db 0, 13, 10, '$'
        
         ...
        
         lea  dx, message
         mov  ah, 09h
         int  21h
         lea  dx, string
         mov  ah, 0Ah
         int  21h
         mov  dl, 10             ; (*)
         mov  ah, 02h
         int  21h
         ; output string char by char 
         mov  si, 2              ; Characters start at offset 2
         lea  dx, TheChar
         xor  cx, cx
         mov  cl, string[1]      ; Count of characters
        output:
         mov  al, string[si]     ; Fetch one character
         mov  TheChar, al
         mov  ah, 09h
         int  21h                ; Print one character plus CR plus LF
         inc  si                 ; Move to next character
         loop output             ; Repeat for all characters
        

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-21
      • 1970-01-01
      • 1970-01-01
      • 2015-09-07
      • 2021-07-20
      • 2019-01-14
      • 1970-01-01
      相关资源
      最近更新 更多