assembly 如何在TASM中逐个字符输出字符串?

t40tm48m  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(179)

我为DOSBox写了一个程序(使用TASM)。我需要将输入的字符串作为字符逐行输出。我弄清楚了如何输入字符串,但我有错误的output
存在2个问题
1.我不知道如何得到length的字符串,所以有太多的空行。
1.在输出中,每行没有一个字符。
我的代码:

.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
krugob8w

krugob8w1#

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

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

“我弄明白了怎么输入字符串了,......”真的没有!
string 行应该定义DOS.BufferedInput函数0Ah的输入结构。DOS应该在第一个字节中找到存储长度,并在第二个字节中返回实际输入的长度。How buffered input works有详细信息。
你为 string 写的内容被翻译成一个10字节的内存区域,完全用数字32(ASCII的' ')填充,乐观地说,后面跟着一个美元字符(链接的帖子解释了为什么这不是一个好主意)。因为第一个字节是32,它将允许DOS合法地使用接下来的34个字节用于输入目的。你有一个严重的缓冲区溢出!
如果要允许用户输入10个字符,则正确的定义为string db 11, 0, 11 dup(0)
存在2个问题
1.我不知道如何得到length的字符串,所以有太多的空行。
1.在输出中,每行没有一个字符。

  1. DOS已经在 string 输入结构的第二个字节中给了你字符串的长度。只需要获取它:一个月一个月一个月一个月一个月一个月
    1.您的程式码(lea dx, string[si]mov ah, 09hint 21h)* 是 * 使用DOS函式(09 h)输出字符字串。您想要什麽?请改用单一字符输出函式:一个月六个月一个月一个月七个月一个月一个月八个月一个月。

您的程式码(含注解)

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
 jcxz done
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
done:

(*)在DOS.BufferedInput函数0Ah结束时,光标将位于当前行的第一列。您不需要输出回车(13)。只需换行(10)就可以了。这对于常规的MS-DOS来说当然是正确的,但是正如@ecm在她的注解中所写的,该规则存在例外。

我的代码

上面的 output 循环每次迭代执行3次系统调用。如果我们将这个数字减少到1,循环的运行速度可以提高10%1。
在每次迭代中,我们将当前字符复制到一个以$结尾的字符串中,该字符串包含回车和换行字节。然后调用一次DOS.printString函数09 h即可完成输出。这种速度上的提高确实有一个缺点:如果输入字符串中嵌入了$字符,则不会显示该字符串。
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
 jcxz done
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
done:
ggazkfy8

ggazkfy82#

下面是如何逐个字符打印字符串:
声明字符串,例如:

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

相关问题