为了提供一些背景知识,我想研究x86指令是如何手动编码/解码的。我遇到了ModR/M
和SIB
字节,似乎了解x86寻址模式对于理解指令编码方案至关重要。
因此,我在Google搜索了x86寻址模式。搜索返回的大多数博客/视频都是8086处理器的寻址模式。通过其中的一些,不同的寻址模式是寄存器,直接,间接,索引,基于等。但是,在引用这些寻址方式时,博客使用的名称不一致。多个不同的源使用多种不同的寻址模式。英特尔手册here中甚至没有提到这些不同的术语。例如,我似乎无法在Intel手册中找到任何称为“直接”或“间接”的寻址模式。另外,Mod
字节中的ModRM
位是2位字段,这使我想知道是否可以使用4种以上的寻址模式。
我的问题是,诸如直接寻址模式之类的术语是间接寻址模式的旧术语,这些术语不再在英特尔手册中使用,而是由公众使用。如果存在技术上确实存在的术语,则可以在手册中找到它们的引用。
最佳答案
大多数形式的x86寻址模式并没有真正的正式名称。除了64位相对RIP寻址外,它们都具有[base + index*scale + disp8/disp32]
形式(或该形式的任何1或2个组件的子集)。 Referencing the contents of a memory location. (x86 addressing modes)。
英特尔确实在卷1的第3.7.5节中正式命名了寻址模式的那些组成部分。它们还使用了寄存器,立即数和内存,但是通常对不同形式的寻址模式没什么大不了的。
ModRM字节中的Mod位是2位字段,这使我想知道是否可以使用4种以上的寻址模式。
Mod使用disp0 / 8/32选择Register vs.Memory。有用于更多模式的“转义”代码
没有位移的[rbp]
模式将意味着存在一个没有基础的disp32。 (这就是为什么在汇编中看到[rbp+0]
的原因:[rbp]
的最佳编码是base = rbp,disp8为0。(请注意,[rbp]
在用作帧指针时没有用。)
base = rsp的ModR / M编码表示有一个SIB字节。
将为index = RSP的SIB编码意味着没有索引。 (鉴于先前的规则,这使得可以对[rsp]
进行编码,而不是对较少使用的[rsp+rsp]
进行编码。)
用英语写汇编语言时,很自然地使用具有明显含义的术语,包括您提到的某些术语。例如,Intel's optimization manual说(我的重点):
2.3.2.4
Micro-op队列和循环流检测器(LSD)
...(具有索引寻址模式的微熔合体在SnB的IDQ中未分层)
...对于以索引寻址为主的代码(通常
发生在数组处理中),使用基址(或基址+位移)的重新编码算法可以
有时通过保持负载加操作和存储指令的融合来提高性能。
索引寻址模式包括使用idx*scale
的任何组合,无论它是与基本reg还是与disp32一起使用,或两者都使用。 (单独的idx
是不可编码的; [rax*1]
实际上是用disp32+idx*1
编码为disp32=0
的。)在某些时候,他们说“带有索引的任何寻址模式”或类似内容,否则可能不清楚它们到底是什么。的意思。当然,testing with performance counters可以验证解释。
但是他们并不会过度地为事物起名字。当没有明显的英语短语时,他们可以贴在某些东西上,然后写道(仍在Sandybridge部分中):
常见的加载延迟为五个周期。使用简单寻址时
模式,基址加偏移量小于2048,则负载延迟可以为四个周期。
在表2-19中,它们有两列,一列用于Base + Offset > 2048;
或Base + Index [+ Offset]
,另一个用于Base + Offset < 2048
,延迟低1个周期(256b AVX负载除外)。 (有趣的是,[rdi+8]
的延迟比[rdi-8]
低1c。)
(从技术上讲,他们可能应该说“位移”,因为整个寻址方式在x86术语中形成一个偏移量或有效地址,当添加到段基时,它会形成一个线性地址。但是“偏移量”也用于描述立即常数。非x86通用术语中的寻址模式的一部分。幸运的是,如今这些天通常不必考虑x86分段。)
在第1卷手册中,英特尔确实使用了您描述的某些术语。他们将寻址模式描述为仅位移组件为“直接”(某种),而[reg]
为“间接”,因为在谈论指令集及其支持的寻址模式时,确实会使用这些术语。
vol.1 3.7.5 Specifying an Offset
以下寻址模式建议用于以下的常见组合:
地址组件。
位移alone仅位移就代表一个
直接(未计算)到操作数的偏移量。因为位移是
编码在指令中,这种地址形式有时是
称为绝对或静态地址。通常用于访问
静态分配的标量操作数。
基数alone仅基数就表示操作数的间接偏移量。 ...
(索引*比例)+位移⎯此地址模式提供了一种索引到静态数组的有效方法...
基础+指数+位移...
基数+(索引∗比例)+位移together一起使用所有寻址组件可提高效率
当数组的元素大小为2、4或8个字节时,对二维数组进行索引。
但是正如您所看到的,它们并没有为更复杂的表单命名。
但是,它们的确可以区分立即数操作,寄存器操作数和内存操作数。 (3.7运营商地址)。但是,通常在使用寄存器编码的r / m32操作数与必须是寄存器的其他操作数之间几乎没有区别。
分支指令术语
分支机构也可以采用直接还是间接。有点像谈论到达下一个将要运行的代码字节的寻址模式。
6.3.7 64位模式下的分支功能
...
地址大小会影响用于JCXZ和LOOP的RCX的大小;它们也会影响内存的地址计算
间接分支。默认情况下,此类地址为64位。但可以通过地址大小将其覆盖为32位
字首。
间接内存是jmp [rax]
,其中RIP的最终值来自内存,而寄存器间接访问分支(如jmp rax
)将RIP = RAX设置为寄存器。 x86没有用于加载/存储的内存间接寻址模式;采取分支之后的代码获取引入了术语中的间接附加级别。 (有点)。
第2卷manual entry for jmp
确实讨论了间接跳转,相对跳转或绝对跳转。 (尽管请注意,x86没有绝对直接近跳转(为此在寄存器中放入地址),只有绝对远地址由立即指针(ptr16:16
或ptr16:32
)或间接与内存位置一起指定。)
描述接近间接跳转jmp r/m32
(或64)时,他们说“在GP寄存器或内存中间接指定的绝对偏移量”。 (“绝对偏移”是相对于CS段基础的)。
分段使x86寻址更难以讨论,特别是在比较可以显式包含段的特殊寻址模式与不包含特定段的特殊寻址模式时。
命名寻址模式被高估
记住x86寻址模式可以根据一般情况的子集进行操作要容易得多,而不是用诸如Indexed,Based之类的名称分别存储所有不同的可能性。
您会在https://www.tutorialspoint.com/microprocessor/microprocessor_8086_addressing_modes.htm或http://www.geeksforgeeks.org/addressing-modes/之类的教程中看到这种东西,它们对寻址模式的分类大有裨益。后者甚至有一个测验,要求您将C语句与某些寻址模式名称匹配。
使用不太灵活的16位寻址模式,您几乎可以尝试命名它们,而“基数索引”和“索引索引”确实为您提供了不同的寄存器选择。但是,在编程时,您真正需要记住的是,它是[bx|bp] + [di|si] + disp0/8/16
的任何子集的选择。这就是di
/ si
(dst / src索引)甚至bx/bp
的名称的方式。
这样的术语在比较不同ISA的功能时可能很有用。例如,Wikipedia says像PDP-8这样的旧ISA大量使用了间接存储,因为它们的寄存器很少,并且寄存器的寻址范围只有8位。
维基百科还说:
请注意,没有一种普遍接受的命名各种寻址模式的方法。
在模式命名上花很多钱是没有道理的。如果要写东西,请确保清楚您的意思,而不必依赖某些术语的特定技术含义。例如如果您说“索引寻址模式”,请确保读者从上下文中知道是否包含base+index*scale
。
我想知道是否某些命名模式的起源是早于8086的8位微控制器。您可能想在https://retrocomputing.stackexchange.com/上进行询问。我对8位CPU上大多数固定的一字节指令可用的寻址模式了解不多。