我正在尝试全面了解X86指令的所有可能寻址模式。从此开始,我研究了Intel IA-32参考和在线找到的多个辅助参考。

我想正确地理解它们,所以这是我的疑问:





mod == 0b11:已访问寄存器中包含的直接值,非常清楚

mod != 0b11:这些都是间接值,可选8位或16位位移到最终值,因此我们引用计算地址中包含的值。

我的疑问:


16位位移是带符号的还是无符号的?例如mov ax, [SI + 40000]mov ax, [SI - 1000]

mod == 0b00 & R/M == 0b110到底是什么情况?它只是一个间接的绝对值,例如mov cl, [1234h],masm编译为8b0e3412: mov cx, WORD PTR ds:0x1234

这些间接寻址是否总是相对于段?从参考上看,听起来好像在16位模式下,一切总是相对于DS的,除非BP包含在间接地址中,否则使用SS(或使用特定的段替代)。因此,基本上[BP+SI+10h]始终表示SS:[BP+SI+10h],其中SS段向左移动4位。
在这种情况下,67h前缀的确切作用是什么?如果我使用67h前缀,那就像用32位寻址切换16位寻址表,反之亦然? (根据当前的执行模式)。
66h呢?它只是改变在16位和32位之间移动的数据的“大小”吗?例如,强制32位操作数大小意味着将选择32位寄存器,并且总是从间接地址中提取4字节的内存,反之亦然?

现在32位寻址模式





mod == 0b11:直接值,至于16位,非常清楚16位寻址情况

mod == 0b00 && R/M == 0b101:R / M不是指定寄存器而是SIB模式,因此我们可以指定基址寄存器+索引寄存器+标度值

这里一切都足够清楚了,我只是想,至于16位是32位位移是带符号的还是无符号的?如果我理解正确,SIB和位移可以轻松组合,例如mod != 0b11 && R/M == 0b100会生成一个带有特定SIB字节和用于签名位移的附加单个字节的[EAX + EBX*2 + 10]。这些值在平面存储空间中是否被视为绝对值,或者在此处也必须考虑段?

#1 楼

这些是一次很多问题,我至少会回答其中一些问题。但是,请不要,除非您自己编写汇编程序或反汇编程序,否则您不应该真正研究每一点的细节。而且,除非您在汇编程序中做了很多编程工作,并且阅读和理解了反汇编的代码,否则您甚至不应该尝试自己编写汇编程序或反汇编程序。

不要误解我,自己编写汇编器可能是一种有趣的教育经历。但是,繁琐的细节并不是您应该学会理解处理器及其汇编语言的第一件事。 16位位移是带符号的还是无符号的都无关紧要。如果溢出,它就会溢出,并且总是切到16位。因此,如果将偏移量0xfff0添加到地址0x1234,则会得到0x1224作为结果。解释为“ 0xfff0等于-0x0010,因此我们从0x10中减去0x1234”还是“将0xfff0添加到0x1234中,得到0x11224并去除溢出位”,这并不重要。或者,如果添加0x89ab0x89ab,则会得到0x1356。准确地说,0x11356的溢出位被剥离。将0x89ab用作(十进制)35243-30293都没关系。可能的结果-35243+35243=7048635243-30293=4950-30293-30293=-60586-都具有相同的表示形式-0x1356-以16位十六进制表示。

是,mod = 0b00和R / M = 0b110只是间接地址。 mov cx, [1234h]mov cx, WORD PTR ds:0x1234是写同一件事的两种方式。注意我将您的cl更正为cx;是否使用8位或16位寄存器是指令的一部分,而不是寻址模式的一部分。如果有寄存器,则寄存器名称中的大小是清楚的,但是在类似mov [1234h],5的指令中,您不知道5是字节,字还是dword值。 mov word ptr ds:1234h, 5明确了这一点。

是的,所有地址都与所选段相关-大多数情况下为ds,如果使用ss,则为bp;如果使用显式覆盖前缀,则为给定寄存器。请注意,没有一种方法可以在16位模式下相对于sp进行索引,并且如果完全使用bp,它始终是R1+R2组合中的第一个寄存器,从而迫使ssbp一起使用。在32位模式下,可能会有更多组合,并且[ebp+ebx]使用ss,而[ebx+ebp]使用ds。 (但是,32位模式也表示保护模式,在大多数病理情况下,操作系统对于ssds以及cs都使用相同的选择器值。请参见下文。) [BP+SI+10h]表示[SS:BP+SI+10h],这表示地址总线上的(SS<<4 + BP + SI + 10h)。请注意,那些16位处理器在地址总线上有20位,这意味着可能发生溢出,并且溢出位也被切断。因此,FFF0:0010和0000:0000实际上是8086上的相同地址-00000-因为100000的第20位被切断。在32位处理器上,该位20实际上存在。这意味着当引入80386时,使用该机制模糊其复制保护的某些程序停止工作。如果IBM尚未发明围绕它的机制-邪恶的A20登机门,那就应该了。如果您愿意,可以使用Google来实现。

前缀66h67h-问其他人。尽管我已经读和写了20多年的汇编代码,但我从来没有理由要学习十六进制字节和处理器指令之间的关系。往上看。好吧,我想有两个例外:90hNOP,而cchINT3。像PQRST50h 51h 52h 53h 54h这样的字节序列是推入寄存器指令,这使它们对于定位过程开始很有用。

在32位模式下,位移与在16位模式下一样,为“有符号”或“无符号”。只需将它们视为要添加的32位值,这可能会导致溢出而被丢弃。

当然,这些值也被认为是与“段”相关的。仅仅32位就意味着受保护的模式,这意味着这些段被称为选择器,具有不同的语义,并且大多数应用程序程序员通常会忽略它们。 (通常)不再重要:

首先,当8086推出时,它是要替换旧的8080处理器(以及与8080兼容的其他公司的Z80,但更好,更成功)。 8080的最大总大小为64 KB,因此程序员不得不将所有内容-代码,数据,堆栈-压缩到这64 KB中,并且在大多数情况下,这64 KB中的一部分由硬件使用,因此您所需要的更少了。

当设计8086和段寄存器时,intel的某人可能会认为“我们为人们提供了更多的空间-64 kb代码和64 kb数据以及64 kb堆栈,因此程序可以更大;我们可以在多个程序之间执行多任务,操作系统将管理段寄存器为每个程序分配空间,每个程序都可以比今天大得多。”

但是实际上,程序很快就变得很大,因此从未使用过“段寄存器仅应与OS有关”的想法。相反,程序必须自己处理段,这对于从编译器生成器到应用程序程序员的每个人来说都是主要的PITA,并且每个人都必须学习并了解它们以完成任何事情。当32位处理器启动时,以线性方式可寻址4 GB时,段突然变得很大,以至于应用程序程序员不再需要关心它们。如今,严格处理各段并将它们分配给内存映射是操作系统的任务,并且由于受保护的模式,程序即使想要也无法更改它们。因此,大多数操作系统所做的是为程序提供一个单一的平面内存块,并且csdsesss相同地映射到该块。您的应用程序仅看到4 GB的可寻址内存(但是,并非所有这些都需要真正映射到物理内存),并且不再与应用程序相关,它使用哪个段寄存器-[DS:1234][ES:1234]相同因为[SS:1234][CS:1234]相同。

例外是新寄存器FSGS,例如,Windows使用FS进行结构化异常处理,而Linux使用GS进行线程本地存储。这些段没有映射到标准的4 GB块,但是应用程序不会注意到,因为这两个寄存器都没有显式的前缀。 (注意ES不能以相同的方式使用,因为默认情况下,像stos[bwd]movs[bwd]之类的指令都使用ES:EDI。)

评论


感谢您提供详细的答案,我非常感谢您,我将花一些时间阅读它。实际上,我对汇编程序非常实用,并且我为模拟目的(Z80,Mos6502)编写了完全可以工作的简单体系结构的汇编程序/ CPU,因此我对细节或语义没有任何问题,只要在一致且明确的条件下进行解释道路。但是我从未接触过x86,现在我意识到了为什么CISC架构和向后兼容性确实是双刃剑。

–杰克
15年11月30日在22:15

:)很高兴在debug.com成为山丘之王时刷新Guntram blohm在堆栈溢出时没有回答

– blabb
2015年12月1日7:34



一切都非常清楚,如果我从反转/编程的角度来看正确的话,仍然使用处于保护模式的段寄存器,但是它们的值只是GDT和LDT内部的索引,并且转换由CPU在内部进行,因此是透明的到正在运行的进程。这意味着即使在保护模式下,移动轴[0x123]默认为移动轴[ds:0x123],但ds内部的值对于实际内存并不意味着任何东西。

–杰克
2015年12月3日14:41