问题描述
我在执行下面的代码时遇到问题
unique proc
invoke lstrlen,esi
cmp eax,1
jle quit
mov ebx,0; prevIoUs iterator
mov edx,0; next iterator
dec eax
mov ecx,eax
inc eax
next:
inc edx
cmp [esi][ebx],[esi][edx]
je skip
cmp
inc ebx
cmp ebx,edx
dec ebx
je skip
inc ebx
mov [esi][ebx],[esi][edx]
skip:
loop next
mov [esi][edx],'0'
quit:
ret
unique endp
我在这里使用间接寻址,所以我希望
cmp [esi][ebx],[esi][edx]
要替换为
cmp ds:[esi][ebx],ds:[esi][edx]
我在哪里错了?
解决方法
常规指令仅限于一个内存操作数
您在问题中指定了x86
标记,这意味着您正在使用Intel x86指令集。
Intel x86指令可以具有多个操作数,在汇编语言中用逗号分隔。操作数可以是:立即,当常量表达式计算为操作码中的内联值时; register ,当值在处理器寄存器中时;或内存(如果该值位于RAM中)。
您不能在单个cmp
指令中使用两个内存操作数。您应该在代码中拆分cmp
指令。而不是希望一次用于两个内存操作数的一条指令,而是使用两条分别具有一个内存操作数和一个寄存器操作数的指令。第一条指令会将内存中的值加载到寄存器中,第二条指令会将另一个内存位置中的值与该寄存器进行比较。
例如,代替一条指令
cmp [esi][ebx],[esi][edx]
使用两条指令:
mov al,[esi+edx]
cmp [esi+ebx],al
字符串指令通过索引寄存器具有两个内存操作数
您可以使用一条cmpsb
指令,与movsb
之类的其他字符串指令一起,在技术上有两个内存操作数的情况下,这是一个例外。但是,如何通过字符串指令寻址操作数的方式由索引寄存器“ esi”和“ edi”(寄存器大小可能不同)固定,分别用于指定第一个和第二个存储器地址。您不能使用其他寄存器。在汇编代码级别,此指令的两种形式允许使用:显式操作数形式和 no-operand 形式(例如cmpsb
)。显式操作数形式允许使用符号来显式指定存储器的第一和第二地址,即cmps byte ptr ds:[esi],byte ptr es:[edi]
。提供此显式操作数格式是为了允许文档,但是此格式中提供的文档可能会误导您,因为符号不必指定正确的源地址和目标地址,并且如果您指定不正确,例如“ eax”而不是“ esi',某些汇编程序(例如Turbo汇编程序版本5.4)可能会忽略此错误,而将使用'esi'。这些用于字符串操作的索引寄存器始终由指令操作码隐式假定,并且已定义,因此您别无选择。尽管可以更改第一个内存地址的段寄存器,但始终由DS:(RSI / ESI / SI)指定第一个内存地址。第二个存储器地址始终由ES:(RDI / EDI / DI)指定,即使段寄存器也没有选择。除此之外,还必须通过cld
或std
指令来设置方向标志,以指定在操作之后是增加还是减少索引寄存器的寄存器。仅两个存储器操作数的比较结果将更新标志,而不更新索引寄存器的增加/减少结果。请注意,并非所有汇编程序都支持显式操作数形式,因此,例如,Netwide汇编器会在任何显式形式的实例上给出错误。尽管对于显式形式,Turbo Assembler将忽略您指定的索引寄存器,但仍将检查指定的段寄存器。如果您要为第二个内存地址指定其他段寄存器,则会出现错误“无法覆盖ES段”。