art of disassembly----chapter01----lesson9--Opcodes and Mnemonics---03

来源:互联网 发布:网络经典语录2017 编辑:程序博客网 时间:2024/05/17 03:49

     扩展Y86指令集

        Y86CPU仅仅适合于模拟如何来编码机器指令。然而,像其它好的CPU一样,它提供了可扩展性。因此,你可以通过增加新的CPU指令来改良这个CPU.

       有两种标准的方式来增加一个CPU指令集中指令的数目。两种途径都要求有未定义的操作码。既然Y86还有一些未定义的操作码,因此我们可以很容易的来扩展它的指令集。

       

        第一种方法是直接使用未定义的操作码来直接定义新的指令。当在一个操作码组中还有未定义的位并且你要定义的新的指令又恰好是属于这个组的话,这种方法最适合不过了。例如:000,11,mmm这个操作码与NOT指令在同一组中。如果你决定在你的指令集中添加NEG指令,那么将这个操作码作为NEG指令的操作码是有很大意义的,因为你可以看到NEG指令与NOT指令使用相同的语法格式。

       不幸的是,Y86当前并没有那么多开放的未定义的操作码。例如,如果你想把单操作数指令SHL,SHR,ROL,ROR指令添加到指令集中,但是当前并没有足够的未定义的操作码来添加这些指令(当前仅剩一个未定义的操作码)。同样的,Y86当前没有未定义的双操作数的操作码存在,因此,如果你想增加双操作数的指令,你就没有那么幸运了。

      解决这种困境的一种通用方式是使用一个操作码前缀字节。这种操作码扩展机制使用未定义的操作码来作为一个前缀字节。当CPU遇到一个前缀字节时,它将把下一个字节当作是真正的操作码并解码之。然而,双字节的操作码对第二个字节的编码使用完全不同于单字节的编码机制,以便你能够在此节字中尽可能多的增加指令。

     例如,操作码$FF未定义,则我们可以使用此字节来作为一个操作码前缀.

       

      


      呼·```经过前面的准备工作。现在我们可以进入最激动人心的时刻了。来看看如何编码X86指令

       编码X86指令

         

           

             显然,Y86处理器简单易懂,并且很容易可以对它进行手工的编码,是一个学习怎样编码机器指令的好工具。现在,是时候来看看X86系列处理器真正的机器指令格式了。

          人们将X86CPU称为复杂指令系统不是没有原因的。虽然更复杂的编码方式确实存在,但是没有人想挑战X86有复杂的指令编码这一事实。X86指令的格式如Figure 5.14所显。虽然这个图示似乎表明X86最长的指令有16个字节,但是在实际上,X86最多的指令只有15个字节。

          

   这里的前缀字节不同于上节所提的操作码扩展前缀。相反的,这些特殊的字节是用来改变现有指令的行为的(而不是定义新的指令)。待会儿我们先来看一组前缀字节,剩余的部分将在以后的章节中讨论。X86系列支持的前缀是多于四个值的(请注意这里是前缀字节的值),但是一条指令最多可以有四个字节的前缀。同时要注意的是,很多前缀所定义的行为是相互排斥的,如果你将一系列相互排斥的前缀放于一条指令的前面,这样的结果是未定义的。

  X86支持两种操作码尺寸:标准的一字节操作码与两个字节的操作码。两个字节的操作码的第一个字节是操作码扩展前缀字节值为$0F,第二个字节则为真正的操作码。解码这些操作码字节的一种方式是将其视为Y86编码中iii域的8位扩展。 这提供了多达512种不同的指令类(虽然X86未全部使用它们)。实际上,各种不同的指令类都使用未定义的指令类的特定位来满足自身的使用需要。例如,ADD指令的操作码如下Figure 5.15所示:

       

       注意的是第0位指定了ADD指令的操作数大小。值0则其操作数的大小为8位,值1则其操作数的大小为16位或32位。在32位的操作系统的环境下,第0位包含1时其默认的操作数大小为32位。要指令一个16位大小的操作数,则需要在指令前面加上一个操作数大小前缀字节

      第1位指定了目的操作数。值0表明目的操作数是内存,值1表明目的操作数是寄存器。等会儿你会发现, 这人位的使用导致了同一条指令那有两个不同的操作码。

      

       编码指令操作数

           'mod-reg-r/m'这个字节指定了基本的寻址模式,这个字节可以分为如下几个域:

        

       

   REG域指定了X86的一个寄存器。这个寄存器可作为目的操作数也可作为源操作数,这取决于具体的指令。很多指令的操作码中都有d域来指令REG域代表的寄存器是源操作数(d=0)还是目的操作数(d=1)。 REG域的位编码如下:

      

      对于特定的指令(单操作数),REG域的值代表的是操作码扩展而不是一个寄存器(在这种情况下,R/M域指定操作数)

        MOD 与R/M域一起指定了双操作数指令的另一个操作数(或是单操作数指令的操作数)。要记住的是,操作码的'd'位指定了哪个操作数是源操数,哪个操作数是目的操作数。MOD R/M域一起指定了如下的寻址模式:

      

      

       

   操作码中的s位来指定使用8位寄存器或32位的寄存器。要使用16位的寄存器,则需给出操作码前缀。

   有几个需要注意的地方。第一,有两种形式的[reg+disp]寻址模式(寄存器相对寻址)。一种形式是带8位偏移,另一种带有32位的偏移。

   第二个要注意的事情是,没有[esp]这种寻址模式(esp寄存器间接寻址)。一看上表你就可以发现,它被32位直接寻址给取代了。寻址模式的基本编码体系不允许有直接寻址,因此,INTEL用直接寻址来取代了[esp]间接寻址。 不过你可以用[esp+disp8](寄存器相对寻址模式)来完全实现[esp]寻址同样的效果,只要把8位的偏移设为0即可。

没错,指令会更长,但是[esp]寻址的效果没有消失。INTEL取代[esp]的模式的原因在于他们预测到程序员使用这种模式模式的频率要大大少于其它的寄存器相对来寻址。

   另外你会注意到的是,[ebx+edx*4](比例变址寻址SIB)这种形式的寻址方式也没有在上表中体现。SIB模式取代了[esp],[esp+disp8].[esp+disp32]这三种寻址模式,由上表可见。当MOD R/M 指定的是SIB寻址模式时,将在MOD-REG-R/M 字节后紧跟着一个字节称为SIBSIB指定了使用到的寄存器。(注意的是,MOD域还是指定了偏移的尺寸,无偏移,8位偏移或是32位偏移) .下表展示了SIB字节的域构成及各个域的含义及其位编码:

                       

            

      

       

         

      

毫无疑问,SIB   MOD-REG-R/M是非常复杂的。这些寻址模式如此复杂的原因在于INTEL在32位模式中重用他们的16位寻址电路而不是在32位模式中放弃16位格式。寻址模式这么复杂有一个很好的硬件理由,但是导致的结果就是一个复杂的体系来指定寻址模式。

     寻址体系如此复杂的部分原因在于SIB与直接寻址模式的存在。直观上,MOD-REG-R/M字节的编码是不允许有直接寻址模式的。INTEL在这个体系中用直间寻址来代替[esp]间接寻址。

    同时你可以注意到 MOD-REG-R/M字节的R/M域如果为100并且MOD不为11时,则此时的寻址模式为SIB模式而不是所期许的[esp] [esp+disp8] [esp+disp32] .

    SIB有如下的寻址模式:

                 

   

注意上表有错,应该把MOD%01对应的表中的reg8改为reg,将disp 改为disp8

                                        把MOD%10对应表中的reg32改为reg,将disp改为disp32

      

          在此来着重讲一下当MOD=00  BASE=101时的情况.此时表明比例变址寻址是没有基地址的情况的(有基地址: [ebx+ebx*4]  无基地址[ebx*4[ ) 则此时的这种无基地址的比例变址寻址一定是会加上一个32位的偏移的。 即 [ebx*4] 实际上是[ebx*4+0] 就如同[esp ]实际上为[esp+0]一样的。

         同样的,当BASE出现在为ebp时,由于其MOD=00已被占用来表示无基址的情况,因此当add eax,[ebp+eax*4]实际上被编码为add eax,[0+ebp+eax*4] 

        哎,这里所用的手段其实与将[esp]处理为[esp+0]是一样的。

       如若大家还不大清楚,再来看看下面几条指令自己再研究下吧·翻译水平太低,着实。。。

                  

    

     

 这些寻址模式,MOD-REG-R/M 中的MOD域都指定了偏移量的大小(0字节,1字节,4字节).即: SIB MOD ,SIB+DISP8 MOD ,SIB+DISP32 MOD。

    SIB字节中的BASE域、INDEX域分别指定了要使用的基地址寄存器与比例寄存器。 要注意的是,ESP寄存器不能作为比例寄存器。INTEL同样是为以后扩展寻址模式而预留的。

   正如MOD-REG-R/M的编码一样,SIB格式重新定义了[EBP+INDEX*SCALE] 为[EBP+INDEX*SCALE_DISP]这样的模式。 如上所解释的,你可以将DISP指定为0 来达到效果。

     

原创粉丝点击