深入理解计算机系统(3.5)------特殊的算术操作指令

  在上一篇博客 算术和逻辑操作 我们介绍了如下图几种常用的算术逻辑指令,但是大家发现没,这几种指令如果在 IA32 上只能操作32位寄存器,比如我用乘法指令IMUL得出的结果超过了32位,那就会产生结果溢出,那应该怎么办呢?

1、特殊的算术操作指令指令

  如上图,上面的几个指令支持有符号和无符号的全64位乘积以及整数除法,但是需要注意的是,存储结果的寄存器固定死了,是一对寄存器%edx(高32位)和%eax(低32位)组成的 64 位的四字。

2、imull 和 mull 指令

  对于 imull 指令,上一章我们在讲算术和逻辑操作指令的时候,讲过这个指令,这是一个乘法指令,指令形式是 imull S D,这里有两个操作数,它将计算S和D的乘积并截断为双字,然后存储在D当中。由于在截断时,无符号以及有符号的二进制序列是一样的,因此此处的乘法指令并不区分有符号和无符号。

  但是对于本章的 imull 指令,它和前面的所表示的乘法指令有所不同,这里只有一个操作数,如上图,它的指令形式是 imull S。但是实际上它还有一个默认的操作数——寄存器%eax,这两者相乘,最终的结果是将高32位存入%edx 寄存器,低 32 位存入%eax 寄存器。

  如果我们只取低 32 位的结果,那么这里指令的意思和上篇博客所讲的乘法指令意思是一样的了,imull S D,只不过这里的D是寄存器 %eax。

  这里我们可以看一个实例:imull $0x3,我们假设此时%eax寄存器的值为0x82345600。也就是我们需要计算0x3*0x82345600的值,这里直接给出两者相乘的16进制表示,为0xFFFF FFFE 869D 0200。这个结果为64位的,因此我们寄存器的前后状态如下所示。

  本实例引用:http://www.cnblogs.com/zuoxiaolong/p/computer17.html

  可以看到,%eax保存着低32位的结果,单说这32位的话,它的有符号数值为-2036530688,正是我们直接计算0x3*0x82345600的32位截断后的有符号值,显然这个结果溢出了。如果组合上高32位,则结果为-6331497984,将它加上或者取模4294967296(2的32次方)将得到我们32位的结果。这里的有符号乘法采取的是先符号扩展被乘数,然后两者相乘,将结果再截断为64位所得。

  对于mull的单操作数指令来讲,就比较简单了,它采用的是无符号乘法,因此就和我们平时的十进制乘法运算类似,只是同样的,它也会将结果的高32位存入%edx,将低32位存入%eax。

  所以虽然 imull 可以用于两种不同的乘法操作,但是汇编器能够通过计算操作数的数据,分辨出想用哪条指令。

3、cltd 指令

  这个指令就是简单的将%eax寄存器的值符号扩展32位到%edx寄存器,也就是说,如果%eax寄存器的二进制序列的最高位为0,则cltd指令将把%edx置为32个0,相反,如果%eax寄存器的二进制序列最高位为1,则cltd指令将会自从填充%edx寄存器为32个1。

4、idivl、divl指令

  这两个指令分别是有符号除法和无符号除法指令,有符号除法指令 idivl 将寄存器 %edx(高32位)和 %eax(低32位)中的64位数作为被除数,而除数作为指令的操作数给出。指令将商存储在寄存器 %eax 中,将余数存储在寄存器 %edx 中。

  比如指令idivl $0x3的结果,我们假设此时%eax寄存器的值为0x82345600。也就是我们需要计算0x82345600/0x3的值,这里直接给出两者相除的16进制表示,商为0xD6117200,余数为0x0。因此我们寄存器的前后状态如下所示。

  可以看到,在idivl这个指令执行的过程中,其实对被除数进行了符号扩展,类似于cltd指令,或者有时也会将%eax移动到%edx,然后对%edx进行算术右移31位的运算。这两种方式的结果是一样的,都是将%eax符号扩展32位并存储在%edx当中。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏pangguoming

ES6的Promise

相信凡是写过javascript的童鞋也一定都写过回调方法(callback),简单说回调方法就是将一个方法func2作为参数传入另一个方法func1中,当fu...

29230
来自专栏iOS开发攻城狮的集散地

run timeimport : 成员变量、类、方法

19290
来自专栏DT乱“码”

Java中实现多线程有两种途径

Java中实现多线程有两种途径:继承Thread类或者实现Runnable接口. Runnable接口非常简单,就定义了一个方法run(),继承Runnable...

22550
来自专栏大前端_Web

详解javascript作用域和闭包

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/article/deta...

12940
来自专栏吾爱乐享

软件测试之学习shell编程函数的使用

13540
来自专栏kalifaの日々

美团北京视频面试题目

1.用过makefile吗 2.python的多线程是真正的多线程吗? 3.写一个冒牌排序,再写一个递归的冒泡排序 4.写一个单链表反转,十几行代码以内 ...

10420
来自专栏JetpropelledSnake

Python入门之函数的介绍/定义/定义类型/函数调用/Return

 本篇目录:     一、 函数的介绍     二、 函数的定义     三、 定义函数的三种类型     四、 函数调用的阶段     五、 Return返回...

38350
来自专栏技术博客

Knockout.Js官网学习(数组observable)

  如果你需要探测和响应一个集合对象的变化,你应该用observableArray 。

17940
来自专栏前端儿

理解运用JS的闭包、高阶函数、柯里化

JS的闭包,是一个谈论得比较多的话题了,不过细细想来,有些人还是理不清闭包的概念定义以及相关的特性。

32930
来自专栏xingoo, 一个梦想做发明家的程序员

const修饰的双重指针赋值解惑

在c程序中,我们可能经常会使用到指针之间的赋值。 传统的赋值操作: char *cp = "c"; const char *ccp; ccp = cp; pri...

23290

扫码关注云+社区

领取腾讯云代金券