大多数形式的 x86 寻址模式都没有真正的正式名称。他们都有这样的形式[base + index*scale + disp8/disp32]
(或其任何 1 或 2 个组件的子集),64 位 RIP 相对寻址除外。看引用内存位置的内容。 (x86 寻址模式)了解您可以对每个子集执行哪些操作的详细信息。
英特尔确实在第 1 卷第 3.7.5 节中正式使用这些名称来表示寻址模式的组件(如下引用)。他们还使用寄存器、立即数和内存来对操作数进行分类,但通常不会对内存操作数的不同形式的寻址模式大做文章。 (在 x86 机器代码中,操作数通常是 r/m,即它可以是 reg 或 mem,具体取决于 ModRM 字节中的 2 位“mod”字段,而另一个操作数绝对是寄存器或绝对是一个立即,如操作码所暗示的。例如,参见的形式add)
ModRM字节中的Mod位是2位字段,这让我想知道是否可以有4种以上的寻址模式。
Mod
使用 disp0/8/32 选择寄存器与内存。有更多模式的“转义”代码
- 的模式
[rbp]
没有位移意味着 disp32 没有底座。 (这就是为什么你看到[rbp+0]
反汇编:最佳编码[rbp]
是base=rbp,disp8为0。(注意[rbp]
当它是帧指针时没有用。)
- ModR/M 编码为 base=rsp 意味着有一个 SIB 字节。
- 索引 = RSP 的 SIB 编码意味着没有索引。 (考虑到前面的规则,这使得编码成为可能
[rsp]
,而不是不太有用的[rsp+rsp]
.)
当用英语撰写有关汇编语言的文章时,很自然地使用具有明显含义的术语,包括您提到的一些术语。例如,Intel优化手册说(我的重点):
2.3.2.4
微操作队列和循环流检测器 (LSD)
...(具有索引寻址模式的微融合微指令在 SnB 上的 IDQ 中未层压)
...对于主要由索引寻址(经常
发生在数组处理中),要使用的重新编码算法基址(或基址+位移)寻址能
有时通过融合加载加操作和存储指令来提高性能。
索引寻址模式包括使用的任何组合idx*scale
,无论它是使用基本 reg 还是使用 disp32,或两者都使用。 (idx
单独是不可编码的;[rax*1]
实际上编码为disp32+idx*1
with disp32=0
.)在某些时候,他们会说“任何带有索引的寻址模式”或类似的内容,否则可能不清楚他们的确切含义。当然,使用性能计数器进行测试可以验证解释。
但他们不会过度地为事物起名字。当没有明显的英语短语时,他们可以坚持某些内容,他们会写道(仍在桑迪布里奇部分):
常见的加载延迟是五个周期。当使用简单寻址时
模式,基址加上小于 2048 的偏移量,加载延迟可以是四个周期。
在表 2-19 中,它们有两列,一列用于Base + Offset > 2048;
or
Base + Index [+ Offset]
,另一个为Base + Offset < 2048
延迟降低 1 个周期(256b AVX 负载除外)。 (有趣的事实,[rdi+8]
延迟比以下低 1c[rdi-8]
.)
(从技术上讲,他们可能应该说“位移”,因为整个寻址模式计算(有效地址)是 x86 术语中 seg:off 逻辑地址的偏移量,当添加到段基址时形成线性地址。但是“offset”还用于描述非 x86 通用术语中寻址模式的直接常量部分。幸运的是,现在 x86 分段并不是您通常需要考虑的问题。)
在第 1 卷手册中,英特尔确实使用了您描述的一些术语。他们将仅具有位移分量的寻址模式描述为“直接”(某种程度上),并且[reg]
称为“间接”,因为在谈论指令集及其支持的寻址模式时确实会使用这些术语。
vol.1 3.7.5 指定偏移量
以下寻址模式建议使用常见的组合
地址组件。
-
移位——单独的位移代表direct操作数的(未计算的)偏移量。因为位移是
编码在指令中,这种形式的地址是有时
称为绝对地址或静态地址。它通常用于访问
静态分配的标量操作数。
-
Base——单独的一个碱基代表一个indirect到操作数的偏移量。 ...
-
(索引 * 比例)+ 位移 — 这种地址模式提供了一种有效的方法来索引静态数组...
-
基数+索引+位移...
-
基址 +(索引 * 比例)+ 位移 — 一起使用所有寻址组件可实现高效
当数组元素大小为 2、4 或 8 字节时,对二维数组进行索引。
但正如您所看到的,它们不会为更复杂的形式命名。
不过,它们确实区分了立即操作数、寄存器操作数和内存操作数。 (3.7 操作数寻址)。不过,他们通常很少或根本不区分使用寄存器编码的 r/m32 操作数与必须是寄存器的其他操作数。
分支指令术语
直接与间接也适用于分支。这有点像谈论到达接下来要运行的代码字节的寻址模式。
6.3.7 64 位模式下的分支函数
...
地址大小影响用于JCXZ和LOOP的RCX的大小;它们还会影响地址计算记忆
间接分支机构。此类地址默认为 64 位;但它们可以通过地址大小覆盖为 32 位
字首。
内存间接是jmp [rax]
,其中 RIP 的最终值来自内存,而不是像这样的寄存器间接分支jmp rax
设置 RIP=RAX。 x86 没有用于数据加载/存储的内存间接寻址模式;采取分支后的代码获取引入了术语中额外的间接级别。 (某种程度上,由于在将新地址加载到 RIP 中后,RIP 被代码提取取消引用)。
第2卷手动输入jmp确实谈论了间接跳跃与相对或绝对跳跃。 (尽管注意 x86 没有绝对的直接near跳转(如果不能使用相对,请将地址放入寄存器和 jmp reg);唯一绝对的直接跳跃是缓慢的“远”jmp ptr16:16
or jmp ptr16:32
立即指针作为机器代码的一部分。)
当描述近间接跳跃时,jmp r/m32
(或 64),他们说“在 GP 寄存器或内存中间接指定的绝对偏移量”。 (此处 seg:off 意义上的“偏移量”将用作 cs:eip 或 cs:rip 的一部分以进行代码获取。)。
分段使得 x86 寻址变得更加复杂,尤其是在比较可以显式包含段的特殊寻址模式与不包含显式段的特殊寻址模式时。
命名寻址模式被高估了
记住 x86 寻址模式在一般情况的子集方面可以做什么要容易得多,而不是用索引、基于等名称单独记住所有不同的可能性。
你会在像这样的教程中看到类似的东西https://www.tutorialspoint.com/microprocessor/microprocessor_8086_addressing_modes.htm or http://www.geeksforgeeks.org/addressing-modes/这对寻址模式的分类非常重要。后者甚至有一个测验,要求您将 C 语句与一些寻址模式名称相匹配。
对于不太灵活的 16 位寻址模式,您可以尝试命名它们,而基于与索引的寻址模式实际上为您提供了不同的寄存器选择。但是当你编程时,你真正需要记住的是,这是你对任何子集的选择[bx|bp] + [di|si] + disp0/8/16
。就是这样di
/si
(dst/src 索引)也许bx/bp
得到了他们的名字。
此类术语可用于比较不同 ISA 的功能。例如,维基百科说像 PDP-8 这样的旧 ISA 大量使用了内存间接,因为它们的寄存器很少,并且寄存器的寻址范围只有 8 位。
维基百科还说:
请注意,没有普遍接受的方式来命名各种寻址模式。
大肆渲染模式的命名是没有意义的。如果您正在写一些东西,请确保您的意思清晰,而不依赖于某些术语的特定技术含义。例如如果您说“索引寻址模式”,请确保读者从上下文中知道您是否包含base+index*scale
or not.
我想知道命名模式的某些愿望是否源于 8086 之前的 8 位微处理器。您可能想在https://retrocomputing.stackexchange.com/。我不太了解 8 位 CPU 上可用的寻址模式,这些 CPU 大多是固定的一字节指令。