我在组装8086中只在一行中显示4个不同的字符串时出现问题。输出应为"您是","名字",&#34 ;中间名","姓氏"。它与前两个一起工作正常,但最后两个与第一个重叠,意思是,"你是"最终被"中间名"重写,并进一步被"姓氏"重写。如果我在最后两行之前使用下一行,它打印出来很好,但我想在一行中显示所有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
答案 0 :(得分:2)
您应该检查调试器,会发生什么。
如果你愿意,你会在进入" Helena"在第一个提示中,string1
地址的内存内容为:
32 07 48 65 6C 65 6E 61 0D 24 24 24 24 ...
关于这些数据的重要之处是值0D
在地址string1 + 2 + 7 - 1
也称为CR(回车)(+2表示字符串数据,+ 7表示输入长度,-1正在回到最后一个角色。)
同样适用于其他两个输入。
一旦你开始输出最后一行,你将在屏幕上写下:
You are Helena
然后出现第一个CR,它会将BIOS光标返回到行首,但它后面没有LF(0Ah
),因此光标不会出现。 t也向下移动一行,而第二个输入的输出只会覆盖行的开头。
<击> 修复:在显示用户输入的每个名称之前,执行:
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]
,只需执行add bx,dx
并使用[bx-1]
然后...我的内存很模糊,我正在做更多的保护模式x86汇编,其中寻址模式更加宽松和普遍。)
如果8086甚至没有movzx
,那么xor bx,bx
mov bl,[dx-1]
可以做同样的事情(从内存中读取字节,将其扩展为单词)。
编辑:实际上你可能想覆盖最后一个字符而不是SPACE ' '
,在名称之间加上空格......在string3
你可以用mov word ptr [bx+dx-1],0A0Dh
覆盖它为新行写入CR + LF对,但是你应该在string3
缓冲区之后再放一个字节,以避免用尽可能长的输入(全50个字符)进行内存覆盖。
<击> 撞击>
编辑:更多评论......
没有任何书籍
互联网的时代。教你8086(我猜可能在emu8086中)本身就是一种残酷的笑话,然后这些知识将再次为你提供与未来相关的任何编程的新视角,所以即使教授8086也是值得的。你应该可以谷歌出一些emu8086教程(虽然我对质量不太了解,但是从一些问题来判断它很难找到仅涵盖基础知识的教程,但是正确......然后那里&# 39;是"The Art of Assembly Language Programming"本书,可以免费阅读其电子表格供个人使用,而且非常详尽......但也很庞大(你可能还想查看16b) DOS版本,如果您的课程非常糟糕,请快速查看几章,找出需要您进行适当学习的区域。)
我有一种感觉,那就是如何将字符串推入堆栈
你没有在你的代码中的任何地方触摸堆栈...在这种情况下你的评论听起来很可怕,你应该真正深入研究那本书,即使它意味着阅读200-300页。毕竟,你没有使用&#34; urgent&#34;,所以你可能有几个月的时间来赶上组装和计算机架构的基础知识。
&#34;空间编辑的修正&#34;:
但是当你用空格覆盖最后一个字符时,如果用户输入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的字符串的另一个字节(长度在字符串本身之前的字节中存储在内存中):
; input:
; dx = address of string ([dx-1] must contain length of string)
; ax = two chars to be written at the end of string (al = first, ah = second)
changeEndOfInputString:
push si ; preserve original si and bx values
push bx
mov si,dx ; use SI for addressing in real mode
xor bx,bx ; bx = 0
mov bl,[si-1] ; bx = (zero extended) string length
mov [si+bx-1],ax ; overwrite last inputted char + one more
pop bx ; restore bx and si and return
pop si
ret
现在显示名称字符串将使用它来在需要的地方添加空格和新行。
;=====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
&#34;姓氏&#34;缓冲区需要52&#39; $&#39;,因为如果用户输入完整的50个字符名称,第50个和第51个字符将被覆盖到CR + LF,所以第52个&#39; $&#39;将保存为int 21h,9
服务的字符串终结符。
第一个+中间名称缓冲区可以使用51&#39; $&#39;,因为输入将使用' '+'$'
进行修改,因此在50个字符输入的情况下,第51个字符将保持设置为&#39; $&#39;即使在修改之后。
还将0Ah
服务缓冲区的第二个字节设置为0
,而不是?
,因为它在某些DOS版本中实际输入值,告诉DOS多少缓冲区内容对于输入编辑有效,并且在调用缓冲区之前不提供任何有效字符串。
最后说明:
crlf db 0ah, 0dh, '$'
这是LF + CR(错误的顺序),DOS&#34;新行&#34;应该是CR + LF,即13,10 .10,13大部分按预期工作,但在装配世界中这是任何方式的错误,你很幸运,它没有更大的影响。 (你在所有字符串定义中都有错误。)
所以我尝试用bx替换它,并且没有错误但它仍然会被覆盖。
你绝对必须学习使用调试器,所以你有机会检查代码在哪里做你想要的,以及内存和寄存器的内容是什么这种情况。
你没有机会在没有调试器的情况下进行程序集编程,只需要我三次读取原始问题并彻底缓慢(大约10分钟)模拟(懒得搜索8086模拟器)并且汇编程序与你的语法兼容)直到我弄清楚为什么你有字符串被覆盖,因为只有CR&#34;从源头上发现并不容易。我做了多年的x86汇编编程,编写了兆字节的ASM源代码。发现这个错误仍然很棘手。如果我有调试器并通过它运行它,我会在用户输入后检查string1
缓冲区的内容后立即发现问题。
或者用不同的例子解释调试器的需要,当我学习ASM编程时,我没有计算机,所以我不得不在纸上编写代码,然后我在学校几分钟就可以尝试通常每周一次。通常它会因为某些错误而崩溃,但我没有足够的时间在机器上调试它,所以我必须在家里找到该文件中的错误(并且1985年左右没有互联网回复询问SO) 。我花了3-5周的时间来修复所有错误的工作版本。如果我随意使用计算机,使用调试器,我可能会在1小时内完成所有修复工作。然后,现在我只是通过阅读源(即使在其他编程语言中)看到许多错误,这就是我的大脑如何追求那篇论文经验,关注每个点,昏迷和数字......