我正在学习 8086,有一个特别的问题困扰着我,我还没有找到任何令人满意的答案。
我知道 CPU 按顺序执行代码,如果要更改代码流,我们希望 IP 指向我们感兴趣的代码所在的新/旧地址。
现在,我的问题是,当我们遇到跳转指令时,为什么我们(我的意思是 CPU)不只是去更新与标签对应的地址的 IP?
当我们遇到跳转指令时,需要在IP上添加一个位移吗?
在我看来
对我来说,这听起来像是更多的工作,然后只是使用与标签对应的地址更新 IP。但是,我确信事情的完成方式一定是有原因的,只是我不知道。
在 8086 中选择这种设计的原因是什么?
最佳答案
您大大高估了解码相对跳跃的 CPU 复杂性成本。
- calculating the displacement(i.e the distance from the jump label to the next instruction after the jump)
- then taking that displacements 2's compliment,
机器代码必须包含步骤 2 的结果(有符号整数相对位移),因此所有这些都在汇编时完成。并且在汇编程序中,减去两个整数地址已经为您提供了您需要的有符号 2 的补码位移。
使用相对位移有真正的优势,因此仅仅为了简化汇编程序的编写而使 ISA 变得更糟是没有任何意义的。您只需要编写一次汇编程序,但在机器上运行的所有内容都受益于更紧凑的代码和位置独立性。
相对分支位移是完全正常的,并且在大多数其他架构中也使用(例如 ARM:https://community.arm.com/processors/b/blog/posts/branch-and-call-sequences-explained,其中固定宽度指令使得直接绝对分支编码无论如何都不可能)。 不使用相对分支编码会使 8086 成为奇数。
更新 : 也许不完全是奇怪的。 MIPS 使用
rel16 << 2
对于 beq
/bne
(MIPS 指令固定为 32 位宽并始终对齐)。但是对于无条件的j
(跳转)指令,它interestingly it uses a pseudo-direct encoding .保留PC的高4位,直接替换PC[27:2]
位与指令中编码的值。 (同样,程序计数器的低 2 位总是 0
。)所以在地址空间的相同 1/16 内,j
指令是直接跳转,不给你位置无关的代码。这适用于 jal
(跳转链接 = call
),making function calls from PIC code less efficient :( Linux-MIPS 过去需要 PIC 二进制文件,but apparently now it doesn't(但共享库仍然必须是 PIC)。CPU运行时
eb fe
, 它所要做的就是将位移添加到 IP
而不是替换IP
.由于非跳转指令已经更新IP
通过添加指令长度,加法器硬件已经存在。请注意 sign-extending 8 位到 16 位(或 32 或 64 位)的位移在硬件中是微不足道的:2 的补码符号扩展只是复制符号位,不需要任何逻辑门,只需将一位连接到休息。 (例如
0xfe
变为 0xfffe
,而 0x05
变为 0x0005
。)8086 非常强调代码密度,提供许多常用指令的简短形式。这是有道理的,因为代码获取是 8086 上最重要的瓶颈之一,因此更小的代码通常是更快的代码。
例如,两种形式的相对
jmp
存在,一个与 rel8(短)和一个与 rel16(近)。 (在后来的 CPU 中引入的 32 位和 64 位模式中,E9
操作码是 jmp rel32
而不是 rel16
,但 EB
仍然是 jmp rel8
,因为函数内的跳转通常在 -128/+ 127)。但是
call
没有特殊的缩写。 ,因为它在大多数时候用处不大。那么为什么它仍然为相对位移而不是绝对位移而烦恼呢?那么 x86 确实有绝对跳转,但仅限于间接或远跳转。 (到不同的代码段)。例如,
EA
操作码是 jmp ptr16:16
: "Jump far, absolute, address given in operand".要进行绝对近跳,只需
mov ax, target_label
/jmp ax
. (或在 MASM 语法中,mov ax, OFFSET target_label
)。相对位移与位置无关
对这个问题的评论提出了这一点。
考虑一个机器代码块(已经组装),在 block 内有一些跳转。如果将整个 block 复制到不同的起始地址(或更改
CS
基地址,以便可以在段的不同偏移量处访问同一 block ),那么只有相对跳转将继续工作。对于labels + absolute addresses要解决同样的问题,必须用不同的
ORG
重新组合代码指示。显然,当您使用远 jmp 更改 CS 时,这不会即时发生!
关于assembly - 为什么 x86 跳转/调用指令使用相对位移而不是绝对目的地?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46184755/