如何使用两个寄存器进行寻址?

问题描述

我在执行下面的代码时遇到问题

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)指定,即使段寄存器也没有选择。除此之外,还必须通过cldstd指令来设置方向标志,以指定在操作之后是增加还是减少索引寄存器的寄存器。仅两个存储器操作数的比较结果将更新标志,而不更新索引寄存器的增加/减少结果。请注意,并非所有汇编程序都支持显式操作数形式,因此,例如,Netwide汇编器会在任何显式形式的实例上给出错误。尽管对于显式形式,Turbo Assembler将忽略您指定的索引寄存器,但仍将检查指定的段寄存器。如果您要为第二个内存地址指定其他段寄存器,则会出现错误“无法覆盖ES段”。