2.4-复杂的x86指令举例

Posted morwing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2.4-复杂的x86指令举例相关的知识,希望对你有一定的参考价值。

  x86作为复杂指令系统的代表,自然会有不少相当复杂的指令。在这一节我们将会看到其中有代表性的一些例子。
  串操作指令是将存储器中的数据串进行每次一个元素的操作。所谓一个元素可以是字节或者是字。这个串可以很长,能够达到64KB,x86提供了5种不同的串操作指令,并且还有3种重复前缀,可以与串操作指令配合使用。这张表就展示了这5种串操作指令和3种重复前缀。我们来选择其中一组进行介绍。这个指令的格式非常简单,没有任何的操作数,它的功能就是在存储器中将指定位置的一个字节单元传送到存储器的另一个指定的位置。与它配合的经常是这个重复前缀REP,x86的体系结构中有很多种的前缀,这个前缀的涵义是当CX寄存器的值不等于0时,就重复执行这个串操作指令。那么很奇怪的是这个指令没有任何操作数。其实大家要注意x86当中有很多这样的没有操作数的指令,但这并不意味着它们比那些有操作数的指令要简单。因为它们不写操作数,不是因为没有操作数,很可能是因为操作数太多了,实在在指令中写不下,因此它们实际上是有一些隐含的操作数。对于这这条串传送指令,它要传送的数据串称为源串。源串的地址默认放在DS:SI这组寄存器指向的位置。而要传送的目的,我们称为目的串地址,默认放在 ES:DI这组寄存器指向的位置,而要传送的串的长度则放在CX寄存器当中。我们可以看到,虽然没有写操作数,但是它实际有5个寄存器作为它的操作数。不仅它有隐含的操作数,还有一些隐含的操作,除了进行串的传送之外,在完成这个操作之后,硬件上还会自动完成这些操作:第一修改SI和DI寄存器,以指向下一个串元素。然后再判断是否使用了重复前缀,如果是,则将CX寄存器的内容减1,需要注意的是这些操作都是硬件自动完成的,不需要程序员在软件中特别指定。 
  我们来看一个例子。假设我们在存储器中要进行一次数据串的传送。源串的位置在12040这个地址开始,一共三个字节,我们希望传送到12060开始的地方。那我们编写的程序是这样的,假设事先已配置好了数据段寄存器DS为1000,这个程序的前两条指令实际是将数据段寄存器的内容传送到附加段寄存器当中。只不过段寄存器之间不能直接传送,所以借用了AX,然后在SI寄存器当中保存源串的偏移地址,在DI寄存器当中放入目的串的偏移地址,这样DS和SI这组寄存器就指向了源串。而ES和DI这组寄存器就指向了目的串。下一条指令CLD,这是确定传送的方向,一会再进行解释。然后在CX寄存器当中存入3,然后才是这条串传送指令。前面加上了重复前缀,这样的配置就相当于连续执行了三次这条串传送指令。当执行第一次传送之后,第一个字节被传送到了目的串的位置,传送完成后,SI和DI自动被增加,CX自动被减1。这些操作都是由CPU完成的。同时我还要说明,所谓的传送这个字节实际上是被CPU发起的向12040地址的读操作,读入到CPU中,再发起一次向12060地址的存储器写操作,写入到对应的字节单元。在第二次传送后,SI和DI又被加1,CX又被减1,第三次传送完之后,虽然SI和DI继续加1,但CX已经减为0,所以不再继续执行。还需要说明一点的是串传送的方向也是可以设置的。如果设置DF=0,则是从源串的低地址开始传送,在传送过程中,SI和DI是自动增量的修改。如果设置DF-=1,则是从源串的高地址开始传送,传送过程中,SI和DI自动减量的修改。这个表格就说明了SI和DI的修改方法。那如何修改DF标志位呢?其实x86提供了两条控制指令,对标志位进行操作。STD就是把DF标志置1。CLD 就是我们刚才的例子中的那条指令,是把DF清0。这就可以确定串传送的方向。设置这样的方向实际上是为了应对源串和目的串有可能重叠的问题。我们简单来看一个释意。如果源串和目的串在内存中是互相不重叠的,那这时候设置DF为0,或者为1,都没有关系。但是如果你的源串和目的串有一个重叠,那必须设置DF为1,从高地址依次向低地址开始传送,不然图中绿色的重叠部分,就会在传送的一开始被覆盖,从而导致结果的错误。那如果源串和目的串是这样的重叠的形式,则必须设置DF为0。 
从低地址开始传送,原因也是一样的。除了串传送指令,还有其他类型的串操作。例如在一个数据串中,查找特定的数据,或者比较两个数据串是否相同。这样程序员有了很便利的手段,对一大块数据进行操作。因此串操作指令是功能非常强大的指令,不过由于数据串当中的元素数量有可能很多,因此串操作指令的执行时间也可能很长,这是需要注意的。
  最后我们从 一个有趣的例子来看一看x86指令的复杂程度。这张图是x86指令的通用格式。每一个小格都是指令格式中特定的位域。那么我们可以人为写出一条指令来,这条指令是一个加法,而且有一个前缀LOCK,这和我们刚才学到的REP一样,都是指令的前缀。这个加法,其中一个源操作数是32位的立即数。另一个源操作数以及目的操作数,是内存当中的一个32位的存储单元。这个存储单元本应默认在数据段,但这里强制指定为在附加段,这个存储单元的地址由EAX寄存器,ECX寄存器和一个立即数计算而得。要计算这个内存地址需要一次乘法,两次加法得到偏移地址,再和段基址进行移位并相加的操作,然后访问这个存储单元得到32位数。在与1 2 3 4 5 6 7 8这个立即数相加,然后再访问这个存储单元,将这个数存进去,这条指令的编码一共有15个字节,可以认为是一条最长的x86指令,x86指令的复杂程度由此可见一斑。
  编程人员只用给出一条简短的指令,计算机就可以完成非常复杂的工作,这自然是一件很好的事情。计算机似乎就应该这么设计,可惜世界没有这么简单, 有人提出了完全相反的做法,我们下一节再说。
 
Reference:北京大学陆俊林老师计算机组成原理课程
Notice:如有侵权,请告知我,我会删除,谢谢!

以上是关于2.4-复杂的x86指令举例的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法学习之排序算法

算法的效率——时间复杂度和空间复杂度

单片机的机器周期和时钟周期分别怎么算的,还请举例说明下?

Linux指令及用法总结(附举例)

Day350.字节码指令集与解析举例 -JVM

五段式指令流水线