我想正确地理解它们,所以这是我的疑问:
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
并去除溢出位”,这并不重要。或者,如果添加0x89ab
和0x89ab
,则会得到0x1356
。准确地说,0x11356
的溢出位被剥离。将0x89ab
用作(十进制)35243
或-30293
都没关系。可能的结果-35243+35243=70486
,35243-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
组合中的第一个寄存器,从而迫使ss
与bp
一起使用。在32位模式下,可能会有更多组合,并且[ebp+ebx]
使用ss
,而[ebx+ebp]
使用ds。 (但是,32位模式也表示保护模式,在大多数病理情况下,操作系统对于ss
和ds
以及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来实现。前缀
66h
和67h
-问其他人。尽管我已经读和写了20多年的汇编代码,但我从来没有理由要学习十六进制字节和处理器指令之间的关系。往上看。好吧,我想有两个例外:90h
是NOP
,而cch
是INT3
。像PQRST
,50h 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时,段突然变得很大,以至于应用程序程序员不再需要关心它们。如今,严格处理各段并将它们分配给内存映射是操作系统的任务,并且由于受保护的模式,程序即使想要也无法更改它们。因此,大多数操作系统所做的是为程序提供一个单一的平面内存块,并且
cs
,ds
,es
和ss
相同地映射到该块。您的应用程序仅看到4 GB的可寻址内存(但是,并非所有这些都需要真正映射到物理内存),并且不再与应用程序相关,它使用哪个段寄存器-[DS:1234]
与[ES:1234]
相同因为[SS:1234]
与[CS:1234]
相同。例外是新寄存器
FS
和GS
,例如,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