一下子有很多问题,我至少会回答其中的一些。但是,请不要,除非您自己编写汇编器或反汇编器,否则您不应该真正深入了解每一位的血腥细节。除非您在汇编程序中进行了大量编程,并且阅读和理解了反汇编代码,否则您甚至不应该尝试自己编写汇编程序或反汇编程序。
不要误解我,写自己的汇编可以是一个有趣和教育经验。但是,要了解处理器的概念及其汇编语言,首先要了解的不是这些血腥的细节。
顺便说一句:
16 位位移是有符号还是无符号都没有关系。如果它溢出,它就会溢出,它总是被削减到 16 位。因此,如果您将 offset 添加0xfff0
到 address 0x1234
,您将得到0x1224
结果。它不一样,如果解释为“真正的问题0xfff0
是相等-0x0010
的,所以我们减去0x10
从0x1234
”或“添加0xfff0
到0x1234
,拿0x11224
,并去掉溢出位”。或者,如果添加0x89ab
和0x89ab
,则会得到0x1356
。0x11356
确切地说,溢出位被剥离。如果您采用0x89ab
as (decimal)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 是字节、字还是双字值。mov word ptr ds:1234h, 5
清楚地说明了这一点。
是的,所有地址都与所选段相关 -ds
在大多数情况下,ss
如果您使用bp
,并且如果您使用显式覆盖前缀,则给定寄存器。请注意,sp
在 16 位模式下没有相对于索引的方法,并且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
——因为第 20 位100000
被切断了。在 32 位处理器上,这个位 20 实际上存在。这意味着一些使用该机制来混淆其复制保护的程序在引入 80386 时停止工作。或者,如果 IBM 没有围绕它发明一种机制 - 邪恶的 A20 门。如果您愿意,请谷歌搜索。
前缀66h
和67h
- 问别人。尽管我已经阅读和编写汇编代码 20 多年了,但我从来没有理由了解十六进制字节和处理器指令之间的关系。往上看。好吧,我想有两个例外:90h
isNOP
和cch
is INT3
。而像PQRST
, 之50h 51h 52h 53h 54h
类的字节序列是压入寄存器指令,这使得它们对于定位过程开始很有用。
在 32 位模式中,位移就像在 16 位模式中一样“有符号”或“无符号”。只需将它们视为添加的 32 位值,这可能会导致溢出并被丢弃。
当然,这些值也是相对于“段”来考虑的。只是那个 32 位意味着保护模式,这意味着这些段被称为选择器,具有不同的语义,并且通常被大多数应用程序程序员忽略。
一个词来细分,为什么它们很重要,并且(通常)不再重要:
起初,当 8086 推出时,它旨在取代旧的 8080 处理器(Z80 来自不同公司,与 8080 兼容,但更好、更成功)。8080 总共最多有 64 KB,因此程序员必须将所有内容——代码、数据、堆栈——压缩到这 64 KB 中,而且大多数情况下,这 64 KB 的一部分被硬件使用,所以你有更少.
当设计 8086 和段寄存器时,英特尔的某个人可能认为“我们给人们提供了更多的空间 - 64 kb 代码和 64 kb 数据和 64 kb 堆栈,因此程序可以更大;我们可以在几个程序,操作系统会管理段寄存器为每个程序分配空间,每个程序都可以比今天大得多”。
但事实上,程序变得越来越大,所以“段寄存器应该只关注操作系统”的想法从未被使用过。相反,程序必须自己处理段,这是从编译器构建者到应用程序程序员的每个人的主要 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
)。