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

The Y86  Hypothetical Processor      Y86------假想的处理器

 

       由于在过去的几年来,80X86处理器功能上的增加、计算机机构的发展及intel在1978年提出的处理器设计目标,使得80X86指令的编码变得非常复杂甚至于在某些方面不符合逻辑。因此,讨论如何设计及编码指令集时80X86并不是一个很好的例子。然而,既然这是讨论80X86汇编语言程序设计,讨论一些现实存在的简单CPU的编码意义不大。因此,我们用两个阶段来讨论指令集的设计:1、我们将为一个基于80X86的简单的假想CPU设计指令集,然后再把我们的讨论扩展到完整的80X86指令集。我们假想的处理器并不是一个真实的80X86CPU,为了避免与80X86系统处理器混淆,我们把这个假想的CPU称为Y86。

      Y86是X86CPU的一个非常简化的版本。首先,Y86只支持一种操作数尺寸----16位。这样的简化使我们免于将操作数的大小编码进操作码中(进而减少我们所需的操作码总数目)另一个简化是:Y86只支持四个16位的寄存器:AX,BX,CX,DX。这样一来,我们用2位就可以来编码所有的寄存器操作数了。(80X86用3个来编码8个通用寄存器)。最后一个简化是Y86只有16位的地址总线,这使得它的最大寻址地址为65536字节。这一系列的简化及一套小小的指令集使我们能够仅仅用一个字节的操作码及两个字节的地址偏移就可以编码所有的Y86指令。 

     Y86CPU提供20条指令,其中7条指令有两个操作数,8条指令有一个操作数,另外5条指令没有操作数。这些指令如下:MOV(两种形式),ADD,SUB,CMP,AND,OR,NOT,JE,JNE,JB,JBE,JA,JAE,JMP,BRK,IRET,HALT,GET,PUT。下面的几小段介绍这些指令的用法。

     MOV指令是涵盖两种指令类型的。MOV指令的两种形式的使用格式如下:

     mov (reg / memory /constant, reg)

     mov (reg , memory )

     reg表示AX,BX,CX,DX,四个寄存器中的一个。memory表示一个内存地址。constant表示一个以十六进制表示的常量

     

    算术运算及逻辑运算有如下的指令格式:

    add(reg / memory / constant , reg )

    sub(reg / memory / constant , reg )

    cmp(reg / memory / constant , reg )

    and(reg / memory / constant , reg )

    or (reg / memory / constant , reg )

    not (reg /memory )

    注意:NOT指令不跟其他的算术指令处于同一个指令类中的,(它只有一个操作数)

    ADD指令将第一个操作数加到第二个操作数上,并把和存在第二个操作数上。SUB指令将第二个操作数减去第一个操作数并把差存于第二个操作数中。CMP指令第一个操作数同第二个操作数进行比较并将比较结果储存以便转移指令的使用。AND OR 指令将两个操作数的各个位进行逻辑比较并把结果储存在第一个操作数中。NOT指令翻转操作数中的各个位。

    控制转移指令可断指令的顺序执行,把控制权从一条指令转移到内存中的其它位置。可以是有条件的转移也可以是无条件的转移。这些指令如下:

        

    以上指令我相信学过X86汇编的童鞋们都了解了。不再进行更多的解释。

    GET PUT指令让你读取和写入整数值。GET指令要求用户输入一个十六进行的整数值并将此值存入AX寄存器。PUT指令由输出AX寄存器中的数值。

   剩下的两条指令HALT BRK不需要操作数。HALT结束程序的执行。BRK暂停程序的执行。

    

    Y86处理器的第一条指令都要一个独一无二的操作码,而不仅仅是指令的类型。虽然mov(bx,ax) 和mov(cx,ax)处于同一指令类,但是他们必然有不同的操作码以便于CPU来区分不同的操作。在看所有可能的操作码之前,我们最好先来看看这些指令所有可能的操作数。

    Y86的寻址方式

       Y86有五种操作数类型:寄存器、常量及三种内存地址表示方法。每一种形式称为一种寻址方式。Y86支持寄存器寻址、立即数(常量)寻址、间接寻址、变址寻址及直接寻址。

      寄存器操作数是很容易理解的,看下面几个例子:

         mov (ax ax );

         mov( bx, ax);

         mov( cx, ax);

         mov(dx, ax);

        第一条指令实际上什么也没有干,它把AX中的值复制到AX中;剩下的三条指令分别将BX,CX,DX中的值复制到AX中。要注意的是BX,CX,DX中的值并未改变。第二个操作数并不AX,可以是任意四个寄存器中的一个。

       常量操作数也很容易理解:

            mov (25 ,ax );

           mov (195 ,bx);

           mov (2056,cx);

           mov (1000,dx);

      这些操作很简单,将数值储存到相应的寄存器中。

      访问内存中的数据时,有三种内存寻址方式,如下:

      mov ( [1000],ax );

      mov ( [bx], ax);

      mov ([1000+bx ],ax);

     第一条指令是用直接寻址的方式,将内存地址1000处的16位数据存入AX寄存器中。第二条指令使用间接寻址,将BX中的值当成地址到对应的地址处取得16位的数据存放入AX寄存器。看如下的例子:

             mov (1000,bx);

              mov ([bx],ax );     这两条指令实际上等同于mov ( [1000],ax);

  当然使用mov([1000],ax)会简洁得多,但是实际在很多场合更合适使用间接寻址。

 最后一种寻址方式是基地变址寻址,如下例:

       mov ( [1000+bx],ax);

       此指令将BX中的值与1000相加后得到最后实际要真正访问的内存地址。这种寻址方式对于数组中元素的访问是很常见的。

      编码Y86指令

        虽然我们可以对每一条指令映射任意操作码(只要每条指令的操作码都各不相同),但是请记住一个真正的CPU是用逻辑电路来解析这些操作码然后再做出相应的操作的。所以一个典型的CPU,操作码中特定的几个位来表明指令的类型,特定的几位来编码操作数。

       Y86的一条完整指令采用如Figure5.3的格式。指令有一个或是三个字节长。指令的操作码占用一个字节,这个字节分为三个域。第一个域(3个bit)---H,O 定义了指令。这提供了8种不同的选择。我们共有20条指令,所以不可能用3个bit来编码所有的20条指令,所以呢,我们将耍些手段来编码其它的指令。如Figure 5.3所示,基本的操作码编码了MOV(两条指令,其中一条rr域指定了目标操作数,另一条的mmm域指定目标操作数),SUB,CMP,AND,OR指令,还有一个特殊的指令型编码000,这个特殊的指令编码提供了扩展可用指令类的数目,稍后再来讨论。

     

      决定一条特定指令的操作码,只需为iii,rr,mmm这三个域指令适当的位值即可。rr域表明目标寄存器(除iii域为111的MOV指令外)mmm域指明源操作数。例如:编码mov bx,ax那么我们如下指定各个域 : iii=110(mov (reg ,reg)), rr=00(ax) mmm=001(bx)因此,此指令的机器码指令为一个字节:110,00,001 

      一些Y86指令的机器码要多于一个字节。例如: mov [1000],ax 的操作码为110,00,110,mov [2000],ax的操作码也为100,00,100,显然这两条指令是不同的。来编码[xxxx]、【xxx+bx]这两种寻址方式或是立即数直接寻址时,需要在紧跟操作码后给出地址的数值。因此,mov [1000],ax 编码后有三个字节:$C6,$00,$10,同样的,mov [2000],ax 编码为:$C6,$00,$20

     前面提到的特殊操作码000可用来扩展X86处理器的可用指令集。这个特殊的操作码处理了数个0操作数及1操作数的指令,如Figure 5.4和Figure 5.5所示。

    

  单一操作数有四个类。00编码进一步用0操作码的指令来扩展指令集,01编码同样的以转移指令来扩展指令集(Figure 5.6)。10编码作为NOT指令的操作码。这条指令按位翻转目标寄存器或目标内存操作数。11编码当前未使用。试图执行这个操作码都会产生一个非法指令的错误而暂停CPU。CPU设计师经常保留未使用的操作码以便于将来扩展CPU的指令集。

   

X86指令集中有数条跳转指令,用如下的形式:

      jxx  address;

     转移指令从当前的执行位置将控制权转移到目标地址处。JMP指令是无条件转移,而其它的六其指令为有条件转移。在此不再进行过多的解释。everybody knows...

    要注意的是本应该有八条转移指令操作码,但X86只使用了7条操作码,第8个操作码同样是未定义的。

    

    最后一组指令无操作数,如Figure 5.5所示。有3个操作码是未定义的。BRK指令暂停CPU直到用户手动重新执行,这个对观察程序执行的中间结果很有用。IRET指令从中断例程中返回。HALT停止程序的运行。GET指令读取用户输入的十六进制数到AX寄存器中,PUT指令输出AX寄存器中的值。

    手动编码指令

     CPU执行的是二系列的二进制数,而不是像mov ax ,bx 这样的指令,所以我们要先将这些人类可读的指令编码为CPU可执行的机器码指令格式。本节我们将来学习如何手动的编码汇编指令。

    第一步选出要编码为机器指令的汇编指令。我们以简单的例子开始:add cx, dx.由上面的几个图可知 ,ADD指令iii域为:101,源操作数为cx,则mmm=010,目标操作数为dx,则rr域为11,由此可得到此条指令的机器码为101,11,010  $BA

    

接下来考虑add (5,ax )这条指令,既然这条指令的源操作数为一个立即数,则mmm域必111,目标操作数为ax rr=00则这条指令的操作码为101,00,111 $A7要注意的是这条指令的编码并未结束。16位的常量$0005也是指令的一部分,在内存中这个常量紧跟着操作码,则这条指令完整的机器码在内存中(地址由低到高)为:$A7,$05,00,

   add ([2ff+bx],cx);也包含了一个16位的常量,这是一个相对基址变址寻址。编码这条指令我们如下指令各个域:

iii=101 rr=10 mmm=101 则有操作码: 101,10,101 完整的机器指令同样要包含$2FF常量,则本条指令完整的机器码为:$B5,$FF,$02

   

现在来考虑一下 add ([1000],ax)这条指令。这条指令将地址$1000处开始的 16bit数据加到ax上。iii=101  rr =00 ,由于寻址方式是立即数直接寻址则mmm=110则得到操作码:101,00,100同样的在随后的两个字节给出16bit的常量,则这条指令的完整机器码为$A6,$00,$10

 

最后一种寻址方式是寄存器间接寻址,[bx].   如下编码add ([bx],bx);这条指令。 mmm=101,rr =01 mmm=100 则本条指令的机器码为一个字节长,因为没带有任何的常量。

 

 SUB ,CMP,AND OR 指令你可以以相同的方式来进行编码,只是说在域iii使用的值不同罢了。

   MOV指令比较特殊一点,因为它有两种使用格式,我们上面只编码了第一种格式。下面我们来看看第二种格式。

   MOV指令的第二种格式的源操作数是一个寄存器而目标操作数是一个内存地址。这种格式的MOV指令,它rr  及mmm域的含义正好相反,rr域表示源操作数,而mmm表示目标操作数。另一个不同在于,mmm域的取值只可能是100[bx]  , 101 [disp+bx]  , 100[disp] ,目标操作数不可能为:000----011 或是111(常量) 原因如下:寄存器不能作为目标操作数因为另于条MOV指令已经处理了,将一个数存入一个常量显然是没有意义的。

 Y86处理器支持只有一个操作数的指令NOT指令。NOT指令有如下两种用法:not (reg ); 或 not (mem);  men代表了三种寻址方式 [bx] [disp+bx] [disp] 要注意的是不可以将一个常州量指定为NOT指令的操作数。

  既然NOT指令只有一个操作数,则它只使用mmm域来编码这操作数。rr 及iii域都为0 (iii=000,rr=10) .每当iii域都是为0时,这表明CPU需要特别的解码这条指令。rr域时来决定的此指令是否为NOT或是其它的需要特别解码的指令。

  编码not (ax)如下。 iii=000,rr=10.mmm域的编码规则同add指令一样。所以对于AX来说mmm=000 则not (ax)的编码如下:000,10,000 $10.

  

  Y86的跳转指令也使用特殊的编码方式。这些指令都是三字节长的,第一个字节(操作码)指定了哪一条跳转指令,后两个字节指出了跳转的地址。Y86共有七条跳转指令,6条条件跳转指令,1条无条件跳转指令。他们iii=000 rr=01而mmm则来编码使用哪一条跳转指令。 编码这些指令是非常直接的,一旦你选定了要编码的跳转指令,则其操作码就已经确定下来了,他们的操作码落入以下范围: $08....$0E

   唯一要注意的是操作码后所接的两个字节的地址。一般来说这里放入的不是一个实际的地址,而是一个偏移地址,这大家都懂的,就不多说了。

   最后一组的无操作数的指令最容易来编码了。由于它们没有操作数,则这些指令都只有一个字节的长度。 它们有如下的域: iii=000,rr=00,mmm域则来指令相应指令的操作码。恨要简单吧。~~!

     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值