汇编中的间接寻址 (x86)
indirect adressing in assembly (x86)
这是间接寻址的格式:
example:
| Base | + Index * Scale | + Discplacement |
|------|-----------------|------------------|
| EAX | EBX * 2 |8-Bit Displacement|
但是有些地方我还不明白:
如果我们在 EAX
中有一个地址并添加 EBX*2
是否会将位于 EBX*2
中的 address/value 添加到 value/address 在 EAX
?
Displacement到底是什么,为什么可能有8位、32位或没有位移,而不是4位或2位位移?
如果我们有每个 4 字节大的值 - 在 EAX
寄存器中我们将乘以 EAX*4
但为什么这个乘法与字节值(我知道每个单元格有 1 个字节)。这是否意味着它会自动将字节转换为位?
完整的公式类似于“address = (base + index << scale + displacement) & mask
”。
注1:“scale”是一个移位计数,没有乘法。它只是为了方便人类而被描述为乘法。
注意 2:“掩码”代表 wrapping/truncation 到目标大小。例如。如果 base + index << scale + displacement
计算出 0x0000000212345678 但 CPU 需要一个 32 位地址,那么掩码将为 0x00000000FFFFFFFF,实际结果将为 0x12345678。
If we have an address in EAX
and add EBX*2
will this take the address/value located in EBX*2
and add it to the value/address in EAX?
是的。例如。如果 EAX = 3 且 EBX = 10,则 EAX+EBX*2 将为 23.
What exactly is the Displacement, why is there 8 Bit,32 Bit or No Displacement possible and not let's say 4 Bit or 2 Bit displacement?
位移只是一个加到其余部分的数字。用于内存访问;它通常用于访问结构字段(例如“结构地址 + 结构中第 n 个字段的偏移量”)和堆栈位置(例如“堆栈顶部 - 从堆栈顶部的偏移量”)。
A CPU 大多只是读取一个数字序列;然后需要能够解码数字以确定指令是什么以及它的操作数是什么(一旦数字被解码,它就会执行相应的指令)。更多的可能性使解码更难,这需要用有用性来证明。 2 位和 4 位位移可以简单地四舍五入到 8 位,因此很难证明解码中的额外复杂性是合理的。
只有 2 位(ModRM 字节中的模式字段)表示寄存器与无显示的内存、8 位显示的内存或 32 位显示的内存。有更多的选择会从其他东西中拿走更多的东西; disp 是一个简单的二进制值(经过符号扩展),它 本身 不使用可变长度编码。 (还好,不然指令长度解码就得看,不能只看前缀+操作码+ModRM字节。)
If we have values that are 4 Byte big each - in the EAX register we would be multiplying EAX*4 but why is this multiplication with byte values (I know each cell has 1 byte). Does this mean that it automatically transforms byte to bit?
“EAX * 4”实际上是“EAX << 2”。小的移位计数非常常见(例如 shl eax,cl
),因为“移位计数太大”(例如在 32 位计算中大于 32)加上对目标大小的屏蔽使得它毫无意义。例如,考虑一个虚构的“address = (EAX << 32) & 0xFFFFFFFF”(这是没有意义的,因为它可以替换为“address = 0”)。
本质上,对于 32 位地址计算,从 0 到 31 的移位计数可能有用,较小的移位计数(从 0 到 3)最有用。此时我们可以回到“更多的可能性使解码更难,这需要用有用性来证明”来理解为什么80x86只支持从0到3(或乘数1、2、4和8)的移位计数;尽管其他班次计数(例如“EAX << 4”或“EAX*16”)偶尔可能会有用。
2、4 和 8 是数组的常见元素大小,因此这个 2 位移位计数使得可以索引双字数组,例如使用保存元素编号而不是字节偏移量的寄存器。
这是间接寻址的格式:
example:
| Base | + Index * Scale | + Discplacement |
|------|-----------------|------------------|
| EAX | EBX * 2 |8-Bit Displacement|
但是有些地方我还不明白:
如果我们在
EAX
中有一个地址并添加EBX*2
是否会将位于EBX*2
中的 address/value 添加到 value/address 在EAX
?Displacement到底是什么,为什么可能有8位、32位或没有位移,而不是4位或2位位移?
如果我们有每个 4 字节大的值 - 在
EAX
寄存器中我们将乘以EAX*4
但为什么这个乘法与字节值(我知道每个单元格有 1 个字节)。这是否意味着它会自动将字节转换为位?
完整的公式类似于“address = (base + index << scale + displacement) & mask
”。
注1:“scale”是一个移位计数,没有乘法。它只是为了方便人类而被描述为乘法。
注意 2:“掩码”代表 wrapping/truncation 到目标大小。例如。如果 base + index << scale + displacement
计算出 0x0000000212345678 但 CPU 需要一个 32 位地址,那么掩码将为 0x00000000FFFFFFFF,实际结果将为 0x12345678。
If we have an address in
EAX
and addEBX*2
will this take the address/value located inEBX*2
and add it to the value/address in EAX?
是的。例如。如果 EAX = 3 且 EBX = 10,则 EAX+EBX*2 将为 23.
What exactly is the Displacement, why is there 8 Bit,32 Bit or No Displacement possible and not let's say 4 Bit or 2 Bit displacement?
位移只是一个加到其余部分的数字。用于内存访问;它通常用于访问结构字段(例如“结构地址 + 结构中第 n 个字段的偏移量”)和堆栈位置(例如“堆栈顶部 - 从堆栈顶部的偏移量”)。
A CPU 大多只是读取一个数字序列;然后需要能够解码数字以确定指令是什么以及它的操作数是什么(一旦数字被解码,它就会执行相应的指令)。更多的可能性使解码更难,这需要用有用性来证明。 2 位和 4 位位移可以简单地四舍五入到 8 位,因此很难证明解码中的额外复杂性是合理的。
只有 2 位(ModRM 字节中的模式字段)表示寄存器与无显示的内存、8 位显示的内存或 32 位显示的内存。有更多的选择会从其他东西中拿走更多的东西; disp 是一个简单的二进制值(经过符号扩展),它 本身 不使用可变长度编码。 (还好,不然指令长度解码就得看,不能只看前缀+操作码+ModRM字节。)
If we have values that are 4 Byte big each - in the EAX register we would be multiplying EAX*4 but why is this multiplication with byte values (I know each cell has 1 byte). Does this mean that it automatically transforms byte to bit?
“EAX * 4”实际上是“EAX << 2”。小的移位计数非常常见(例如 shl eax,cl
),因为“移位计数太大”(例如在 32 位计算中大于 32)加上对目标大小的屏蔽使得它毫无意义。例如,考虑一个虚构的“address = (EAX << 32) & 0xFFFFFFFF”(这是没有意义的,因为它可以替换为“address = 0”)。
本质上,对于 32 位地址计算,从 0 到 31 的移位计数可能有用,较小的移位计数(从 0 到 3)最有用。此时我们可以回到“更多的可能性使解码更难,这需要用有用性来证明”来理解为什么80x86只支持从0到3(或乘数1、2、4和8)的移位计数;尽管其他班次计数(例如“EAX << 4”或“EAX*16”)偶尔可能会有用。
2、4 和 8 是数组的常见元素大小,因此这个 2 位移位计数使得可以索引双字数组,例如使用保存元素编号而不是字节偏移量的寄存器。