我正在学习8086,有一个特别的问题困扰着我,我还没有找到令人满意的答案。
我知道CPU按顺序执行代码,如果想更改代码流,我们希望IP指向我们感兴趣的代码所在的新/旧地址。
现在,我的问题是,为什么当我们遇到跳转指令时,我们(我的意思是CPU)不去用标签对应的地址更新IP?
当我们遇到跳转指令时,有什么必要增加到IP中的位移?
照我的想法
对我来说,这听起来像是更多的工作,而不是只是更新IP与相应的地址标签。但是,我相信这样做一定有原因,只是我不知道而已。
在8086,这种设计选择的原因是什么?
发布于 2017-09-14 06:32:09
您大大高估了解码相对跳跃的CPU复杂度的成本。
机器代码必须包含步骤2(有符号整数相对位移)的结果,所以所有这些都是在组装时完成的。在汇编程序中,减去两个整数地址已经给出了你需要的有符号2的补码位移。
使用相对位移有真正的好处,因此仅仅为了简化汇编程序的编写而使ISA变得更糟是没有任何意义的。您只需要编写一次汇编程序,但是机器上运行的一切都得益于更紧凑的代码和位置独立性。
相对分支位移是完全正常的,并且在大多数其他架构中也使用(例如ARM:https://community.arm.com/processors/b/blog/posts/branch-and-call-sequences-explained,其中固定宽度的指令使得直接绝对分支编码无论如何都不可能)。--它将使8086成为而不是的单数,使用相对分支编码.
update:也许并不完全是奇怪的。MIPS使用rel16 << 2进行beq / bne (MIPS指令固定在32位宽且始终对齐)。但是对于无条件的j (跳转)指令,它是有趣的是,它使用了伪直接编码。。它保持了PC机的高4位,直接用指令中编码的值代替PC[27:2]位。(同样,程序计数器的低2位总是0。)因此,在相同的地址空间的1/16范围内,j指令是直接跳转,不提供与位置无关的代码。这适用于jal (跳转链接= call)、使PIC代码中的函数调用效率降低 :( Linux以前需要PIC二进制文件、但显然现在不是 (但共享库仍然必须是PIC) )。
当CPU运行eb fe时,它所要做的就是将位移添加到IP中,而不是替换IP。由于非跳转指令已经通过增加指令长度来更新IP,加法器硬件已经存在.
请注意,符号延伸 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操作码是“跳远、绝对、以操作数表示的地址”。。
要做一个绝对的近距离跳跃,只需mov ax, target_label / jmp ax。(或者用MASM语法,mov ax, OFFSET target_label)。
相对位移是位置无关的。
对这个问题的评论提出了这一点。
考虑一个机器代码块(已经组装好了),其中有一些跳转。如果您将整个块复制到不同的起始地址(或更改CS基本地址,以便在与段的偏移量不同的情况下访问同一个块),那么只有相对跳转才能继续工作。
要使标签+绝对地址解决同样的问题,必须用不同的ORG指令重新组装代码。显然,当你用一个远jmp改变CS时,这是不可能发生的!
https://stackoverflow.com/questions/46184755
复制相似问题