String 为什么在程序集中我的输出中有多个字符串重叠/覆盖?

String 为什么在程序集中我的输出中有多个字符串重叠/覆盖?,string,assembly,output,x86-16,overwrite,String,Assembly,Output,X86 16,Overwrite,在汇编8086中,我在一行中显示4个不同的字符串时遇到问题。输出应该是“youare”、“first name”、“middle name”和“last name”。前两个很好,但后两个与前一个重叠,意思是“You is”最终被“middle name”重写,然后被“last name”重写。如果我在最后两行之前使用下一行,它会很好地打印出来,但我希望在一行中显示所有4个字符串,而不是在3行中显示。我尝试搜索网络,但大多数答案仅限于显示2个字符串 ;=====output====== mov

在汇编8086中,我在一行中显示4个不同的字符串时遇到问题。输出应该是“youare”、“first name”、“middle name”和“last name”。前两个很好,但后两个与前一个重叠,意思是“You is”最终被“middle name”重写,然后被“last name”重写。如果我在最后两行之前使用下一行,它会很好地打印出来,但我希望在一行中显示所有4个字符串,而不是在3行中显示。我尝试搜索网络,但大多数答案仅限于显示2个字符串

;=====output======

mov ah, 09
mov dx, offset crlf ;next line
int 21h

mov ah, 09
mov dx, offset msg4             ;displays "You are"
int 21h

mov ah, 09
mov dx, offset string1 + 2      ;displays inputted "first name"
int 21h 

mov ah, 09
mov dx, offset string3 + 2       ; this should appear next to string1,  
int 21h                           not rewrite msg4...

mov ah, 09
mov dx, offset string2 + 2       ; this should appear next to string3, not 
int 21h                             rewrite msg4 and string3
这就是输出结果:

Enter 1st name: Helena
Enter last name: Ramos
Enter middle name: Ang

Ramosre Helena                    ;"Ang" rewrites "You are", and then 
                                    "Ramos" rewrites it again

                                 ; This is what I want to see: 
                                 ;     You are Helena Ang Ramos
我在汇编方面几乎是个新手,我的教授也不是最有帮助的老师,因为我们没有任何书籍,课堂讲义只定义了说明,实验练习几乎是复制粘贴他编写的代码,所以我的大多数同学在实际编程中都是自学的。这只是作业的一小部分,实际作业要求我们显示中间的首字母而不是中间的名字,但我甚至不能让它正确显示所有4个字符串!在这一点上,我有一种感觉,字符串在堆栈中的推进方式存在问题,但我有限的知识使我无法找出原因

完整的代码,如果您感兴趣:

org  100h   
.model small
.stack 200

.data
msg1 db "Enter 1st name: $"
string1 db 50,?,50 dup ('$')
msg2 db 0ah, 0dh, "Enter last name: $"
string2 db 50,?,50 dup ('$')
msg3 db 0ah, 0dh, "Enter middle name: $"
string3 db 50,?,50 dup ('$')

msg4 db 0ah, 0dh, "You are $"

crlf db 0ah, 0dh, '$'

.code

mov ax, @data
mov ds, ax

mov ah, 09
mov dx, offset msg1
int 21h

mov ah, 0ah
mov dx, offset string1 ;input first name
int 21h

mov ah, 09
mov dx, offset msg2
int 21h

mov ah, 0ah
mov dx, offset string2 ;input last name
int 21h

mov ah, 09
mov dx, offset msg3
int 21h

mov ah, 0ah
mov dx, offset string3 ;input middle name
int 21h

;=====output======

mov ah, 09
mov dx, offset crlf ;next line
int 21h

mov ah, 09
mov dx, offset msg4             ;displays "You are"
int 21h

mov ah, 09
mov dx, offset string1 + 2      ;displays inputted "first name"
int 21h 

mov ah, 09
mov dx, offset string3 + 2       ; this should appear next to string1,  
int 21h                           not rewrite msg4...

mov ah, 09
mov dx, offset string2 + 2       ; this should appear next to string3, not 
int 21h                             rewrite msg4 and string3

您应该签入调试器,看看会发生什么

如果您愿意,您将看到在第一个提示符中输入“Helena”后,
string1
地址处的内存内容是:

32 07 48 65 6C 65 6E 61 0D 24 24 24 24 ...
关于这些数据,重要的是地址
string1+2+7-1
处的值
0D
也称为CR(回车符)(+2表示字符串数据,+7表示输入长度,-1表示移回最后一个字符)

同样的情况也适用于其他两个输入

一旦开始输出最后一行,您将在屏幕上写入:

您是Helena
,出现了第一个CR,它会将BIOS光标返回到行的开头,但后面没有LF(
0Ah
),因此光标不会向下移动一行,而第二个输入的输出只会覆盖行的开头

修复:在显示用户输入的每个名称之前,请执行以下操作:

    mov ah, 09
    mov dx, offset string1 + 2      ;displays inputted "first name"

    movzx bx, byte ptr [dx-1]       ; bx = length of input
    mov byte ptr [bx + dx - 1], '$' ; overwrite last input char (most likely CR) with '$'
    int 21h 
(如果实际模式不支持寻址模式
[bx+dx-1]
,只需添加bx,dx并使用
[bx-1]
,然后…我的内存很模糊,我在做更多的受保护模式x86汇编,其中的寻址模式更加轻松和通用)

如果8086甚至没有
movzx
,那么
xor bx,bx
mov bl,[dx-1]
也可以做同样的事情(从内存读取字节,零扩展到字)

编辑:实际上,您可能想要覆盖最后一个字符,而不是使用空格
'
,以便在名称之间留出空格。。。在
string3
处,您可以使用
mov word ptr[bx+dx-1],0A0Dh
覆盖它,以便在其结尾处写入新行的CR+LF对,但随后您应该在
string3
缓冲区后多放一个字节,以避免使用最长的输入(满50个字符)进行内存覆盖


编辑:更多评论

我没有书

现在是互联网时代。教你8086(我想可能是在emu8086中)本身就是一个残酷的笑话,那么这一知识将再次为你提供未来任何编程相关的新视角,因此即使教8086也是值得的。你应该能够谷歌出一些emu8086教程(虽然我不确定质量,从一些问题上判断,所以很难找到只涵盖基础知识的教程,但正确地…然后是这本书,它是免费阅读的电子形式供个人使用,它是非常彻底和详细的…但也是巨大的(如果你的课程真的那么糟糕,你可能仍然想查看16b DOS版本,并快速浏览几章,以确定你需要适当学习的领域)

我有一种感觉,在堆栈中如何推动字符串存在问题

你在代码中的任何地方都没有触及堆栈…鉴于你的评论听起来相当可怕,你可能应该真正深入阅读这本书,即使它意味着阅读200-300页。毕竟,你没有使用“紧急”,所以你可能有几个月的时间来掌握汇编和计算机体系结构的基本知识


修正“空间编辑”:

但当您用空格覆盖最后一个字符时,如果用户输入50个字符长的名称,则名称将不再以
'$'
终止,因此您应该将缓冲区定义为在缓冲区之外有固定终止符,如下所示:

string1 db 50,0,51 dup ('$')   ; after 50 byte buffer there's one more '$'
这是ASM编程中最难的部分之一,以避免因错误的数据定义和不安全的内存使用而导致的任何缓冲区/堆栈溢出错误。始终在调试器中使用最小/最大输入测试代码,并观察内存内容,以查看其行为是否符合预期,或者是否发生了意外的内存覆盖以及在何处。您可以可能还需要在缓冲区之间定义一些保护值,例如:

string1 db 50,0,51 dup ('$')
    db 0FFh
msg2 db 0ah, 0dh, "Enter last name: $"
然后,如果您在调试器中看到某个角落大小写输入导致
FF
字节消失,您就知道源代码错误(例如,如果第一个字节是52,用户将输入52个字符长的名称)


使用有效的8086代码修复实模式(原始建议的寻址模式
[dx]
,在实模式下不合法):

首先在代码末尾添加一个过程,该过程将覆盖最后输入的字符+类似pascal的字符串的一个字节(长度存储在内存中,字节位于字符串本身之前):

现在,名称字符串的显示将使用它在需要的地方添加空间和新行

    ;=====output======

    ;display "You are "
    mov ah, 9
    mov dx, offset msg4
    int 21h

    ;display inputted "first name" with space added
    mov dx, offset string1 + 2
    mov ax, 2420h   ; 20h = ' ', 24h = '$' (ASCII encoding)
    call changeEndOfInputString
    mov ah, 9
    int 21h 

    ;display inputted "middle name" with space added
    mov dx, offset string3 + 2
    mov ax, 2420h   ; 20h = ' ', 24h = '$' (ASCII encoding)
    call changeEndOfInputString
    mov ah, 9
    int 21h

    ;display inputted "last name" with CR+LF added
    mov dx, offset string2 + 2
    mov ax, 0A0Dh   ; 0Dh = CR, 0Ah = LF (DOS "new line")
    call changeEndOfInputString
    mov ah, 9
    int 21h
并确保字符串缓冲区的末尾有额外的字节以适应这些修改
    ;=====output======

    ;display "You are "
    mov ah, 9
    mov dx, offset msg4
    int 21h

    ;display inputted "first name" with space added
    mov dx, offset string1 + 2
    mov ax, 2420h   ; 20h = ' ', 24h = '$' (ASCII encoding)
    call changeEndOfInputString
    mov ah, 9
    int 21h 

    ;display inputted "middle name" with space added
    mov dx, offset string3 + 2
    mov ax, 2420h   ; 20h = ' ', 24h = '$' (ASCII encoding)
    call changeEndOfInputString
    mov ah, 9
    int 21h

    ;display inputted "last name" with CR+LF added
    mov dx, offset string2 + 2
    mov ax, 0A0Dh   ; 0Dh = CR, 0Ah = LF (DOS "new line")
    call changeEndOfInputString
    mov ah, 9
    int 21h
string1 db 50,0,51 dup ('$')   ; first name buffer
string2 db 50,0,52 dup ('$')   ; last name buffer
string3 db 50,0,51 dup ('$')   ; middle name buffer
crlf db 0ah, 0dh, '$'