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

Posted

技术标签:

【中文标题】如何使用两个寄存器进行寻址?【英文标题】:How to use two registers for addressing? 【发布时间】:2020-10-30 14:26:55 【问题描述】:

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

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]

我哪里错了?

【问题讨论】:

在同一条指令中不能有多个内存操作数。 @fuz,如果寄存器 ebx 中有地址,寄存器 edx 中有移位,如何获取元素? 您可以像这样使用mov 指令:mov al, [esi][ebx]。虽然使用movzx 会稍微快一些:movzx eax, [esi][ebx]。大多数指令最多支持一个内存操作数。您的比较结果如下所示:movzx eax, [esi][ebx]; cmp al, [esi][edx]. 尝试使用两个内存操作数基本上是Why isn't movl from memory to memory allowed?的重复。我的回答确实显示了[reg+reg] 语法。还有Referencing the contents of a memory location. (x86 addressing modes) 用于寻址模式语法。 【参考方案1】:

常规指令仅限于一个内存操作数

您在问题中指定了x86 标签,这意味着您使用的是 Intel x86 指令集。

一条 Intel x86 指令可以有多个操作数,在汇编语言中用逗号分隔。操作数可以是:立即,当常量表达式计算为操作码中的内联值时; 寄存器,当一个值在处理器寄存器中时;或 内存,当值在 RAM 中时。

您不能在单个cmp 指令中使用两个内存操作数。您应该在代码中拆分 cmp 指令。不要将一条指令同时用于两个内存操作数,而是使用两条指令,每条指令都有一个内存操作数和一个寄存器操作数。第一条指令将内存中的值加载到寄存器中,第二条指令将来自另一个内存位置的值与该寄存器进行比较。

例如,而不是单个指令

cmp [esi][ebx], [esi][edx]

使用两条指令:

mov al, [esi+edx]
cmp [esi+ebx], al

字符串指令通过索引寄存器有两个内存操作数

您可以使用cmpsb 指令,与其他字符串指令(如movsb)一起,在技术上它有两个内存操作数是一个例外。但是如何通过字符串指令寻址操作数的模式由索引寄存器“esi”和“edi”(寄存器大小可能不同)固定,分别指定第一个和第二个内存地址。您不能使用其他寄存器。在汇编代码级别,允许此指令的两种形式:显式操作数形式和无操作数形式(例如cmpsb)。显式操作数形式允许使用符号来显式指定内存的第一个和第二个地址,即cmps byte ptr ds:[esi], byte ptr es:[edi]。提供此显式操作数形式是为了允许文档化,但此表单中提供的文档可能会产生误导,因为符号不必指定正确的源地址和目标地址,如果您指定不正确,例如 'eax' 而不是 ' esi',此错误可能会被某些汇编程序忽略,例如 Turbo Assembler Version 5.4,而将使用 'esi'。这些用于字符串操作的索引寄存器始终由指令操作码隐式假定,并且已定义,因此您别无选择。第一个内存地址始终由 DS:(RSI/ESI/SI) 指定,尽管您可以更改第一个内存地址的段寄存器。第二个内存地址总是由 ES:(RDI/EDI/DI) 指定,即使对于段寄存器也没有选择。除此之外,您还必须通过cldstd 指令设置方向标志,以指定在操作后应该增加还是减少索引寄存器。只有两个内存操作数的比较结果会更新标志,而不是索引寄存器的增加/减少的结果。请注意,并非所有汇编程序都支持显式操作数形式,因此例如,Netwide 汇编程序在显式形式的任何实例上都会给出错误。尽管对于显式形式,Turbo Assembler 将忽略您指定的索引寄存器,但无论如何它都会检查指定的段寄存器。如果为第二个内存地址指定其他段寄存器,则会报错“Can't override ES segment”。

【讨论】:

cmpsb 可以采用段覆盖前缀来将 ds 以外的段用于“源”操作数。写其中,cmpsb 实际上就像(不存在的)cmp byte [si], byte [es:di](加上索引寄存器递增或递减而不影响标志)。即(r/e)si所指向的字节实际上被当作比较的“目的操作数”。 @ecm 非常感谢您指出这一点。我已经相应地更新了回复。

以上是关于如何使用两个寄存器进行寻址?的主要内容,如果未能解决你的问题,请参考以下文章

计算机是如何工作的

x86汇编如何查看一个地址的值

在间接寻址中非法使用寄存器

keil中 如何向指定地址写入数据

如何用汇编语言读取内存值

Linux汇编教程04:寻址方式