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

How to use two registers for addressing?

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

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指令可以有多个操作数,在汇编语言中用逗号分隔。操作数可以是:immediate,当常量表达式计算为操作码中的内联值时; register,当值在处理器寄存器中时;或 memory,当值在 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指令设置方向标志,以指定索引寄存器寄存器在操作后是应该增加还是减少。只有两个内存操作数的比较结果才会更新标志,而不是变址寄存器increase/decrease的结果。请注意,并非所有汇编器都支持显式操作数形式,因此,例如,Netwide 汇编程序会在任何显式形式的实例上给出错误。尽管对于显式形式,Turbo Assembler 将忽略您指定的变址寄存器,但无论如何它都会检查指定的段寄存器。如果为第二个内存地址指定其他段寄存器,则会报错“无法覆盖 ES 段”。