汇编 —— 算术和逻辑操作

写在前面:从腾讯实习回来之后,就感觉到自己的知识体系过于散乱。于是萌生了写一个自己的操作系统这样的心思,此为系列第一章,主要是讲解一些汇编知识的,内容大多从CSAPP中也可以获得。
本篇内容主要讲解汇编指令:算数以及逻辑相关操作指令

加载有效地址

加载有效地址指令leaq实际上是movq指令的变形。 它的指令形式是从内存读取数据到寄存器。它的第一个操作数看上去是一个内存引用,但该指令实际是将有效地址写入到目的操作数。
其使用格式如下:

指令效果描述
leaq S,DD⬅&S加载有效地址

比如说我们下面的这个例子:

long scale(long x,long y,long z)
{
    long t = x+4*y+12*z;
    return t;
}

我们展示其反汇编代码:

//long scale(long x,long y, long z)
//x in %rdi,y in %rsi,z in %rdx

leaq (%rdi,%rsi,4),%rax		//%rax: 4y+x
leaq (%rdx,%rdx,2),%rdx		//%rdx: 3z
leaq (%rax,%rdx,4),%rax		//%rax: 12z+4y+x
ret							//return %rax

leaq 练习题

刚刚讲了leaq的基础用法,现在做两个题巩固一下,还是那样:往下滚动查看答案和解析:
在这里插入图片描述
在这里插入图片描述

练习题答案

在这里插入图片描述
在这里插入图片描述

一元操作符&二元操作符

一元操作符,顾名思义:只有一个操作数,即是源又是目的。比如incq(%rsp)会使栈顶的8字节元素+1。

指令效果描述
inc DD⬅D+1加1
dec DD⬅D-1减1
neg DD⬅-D取负数
not DD⬅~D取补

二元操作符,则是第二个操作数既是源又是目的。比如指令subq %rax,%rdx 使寄存器%rdx的值减去%rax的值,等价于:%rdx = %rdx-%rax


第一个操作数可以是立即数、寄存器或者内存位置
第二个操作数可以是寄存器或内存位置
但是!当第二个操作数为内存地址时,处理器必须从内存读出值,进行操作之后再写入内存中

指令效果描述
add S,DD⬅D+s
sub S,DD⬅D-s
imul S,DD⬅D*s
xor S,DD⬅D^s异或
or S,DD⬅Dls
and S,DD⬅D&s

一元&二元 练习题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

练习题答案

在这里插入图片描述
在这里插入图片描述

移位操作

移位操作会先给出移位量,然后第二项给出的是要移位的数。移位量可以是一个立即数或者放在单字节寄存器%cl中。当移位操作对 w 位长的数据值进行操作,移位量是由%cl寄存器的低 m 位决定的,这里2m = w,而高位则会被忽略。所以,当寄存器%cl中的值为0xFF时,指令salb会左移7位,salw会左移15位,sqll会左移31位,salq会左移63位。

如下表所示,左移指令有两个名字:salshl。两者的效果是一样的,都是将右边填上0 。右移则是不同,sar执行算术移位,会将扩充位填充符号位,而shr执行逻辑移位,将扩充位填上0。

指令效果描述
sal k,DD⬅D<<k左移
shl k,DD⬅D<<k左移(同sal)
sar k,DD⬅D>>k算数右移
shr k,DD⬅D>>k逻辑右移

移位练习题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

练习题答案

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

特殊的算数操作符

下表中的指令提供了一些不常用的特殊的算数操作:
在这里插入图片描述
可以在表中看到有两个乘法指令:imulqmulq。一个是有符号乘法,一个是无符号乘法,这两个可以用来计算两个64位的值的全128位乘积。这两个指令都要求一个参数必须在寄存器%rax中,而另一个作为指令的源操作数给出。之后,乘积存放在寄存器%rdx(高64位)和%rax(低64位),下面的例子给出了如何计算乘积:

#include <inttypes .h>
typedef unsigned __int128 uint128_ t;
void store_uprod(uint128 _t *dest, uint64_t x,uint64_t y) {
*dest = x * (uint128_t) y;
}

这个例子得到的汇编代码如下:

//dest in %rdi,x in %rsi,y in %rdx
store_uprod:
movq %rsi, %rax			//%rax = x
mulq %rdx				//%rax = %rax*%rdx
movq %rax, (%rdi)		//低64位存储在dest中
movq %rdx, 8(%rdi)		//高64位存储在dest+8中
ret						

到目前为止,我们都没有介绍除法操作符和取模操作。这些操作是由单操作数除法指令所提供。有符号除法指令idivl将寄存器%rdx(高64位)和%rax(低64位)中的128位数作为被除数,而除数作为指令的操作数给出。指令将商存储在寄存器%rax,将余数存在寄存器%rdx。以下例子给出了除法的实现:

void remdiv(long x,long y,long *qp,long*rp)
{
    long q = x / y;
    long r = x % y;
    *qp = q;
    *rp = r;
}

其汇编实现如下:

//x in %rdi,y in %rsi,qp in %rdx,rp in %rcx
remdiv :
movq %rdx, %r8			//在%r8中存储qp
movq %rdi, %rax			//%rax = x
cqto					//符号扩展x为8字
idivq %rsi				//执行x/y操作,商在%rax中,余数在%rdx中
movq %rax, (%r8)		//存储商到%r8中
movq %rdx, (%rcx)		//存储余数在%rcx中
ret


无符号除法使用divq指令,通常寄存器%rdx会事先设置为0

练习题

在这里插入图片描述

练习题答案

//x in %rdi,y in %rsi,qp in %rdx,rp in %rcx
movq %rdx, %r8			//在%r8中存储qp
movq %rdi, %rax			//%rax = x
movl $0, %edx			//无符号除法特性
divq %rsi				//执行x/y操作,商在%rax中,余数在%rdx中
movq %rax, (%r8)		//存储商到%r8中
movq %rdx, (%rcx)		//存储余数在%rcx中
ret

参考文献

[1] 深入理解计算机系统 第三章 程序的机器级表示
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
MIPS32汇编语言是一种常用的低级编程语言,用于编写针对MIPS架构的程序。下面是用MIPS32汇编语言实现32位算术逻辑运算的一般步骤: 1. 加法运算: - 使用`add`指令将两个操作数相加,并将结果存储在目标寄存器中。 - 例如,要将寄存器`$t0`和`$t1`中的值相加,并将结果存储在`$t2`中,可以使用以下指令:`add $t2, $t0, $t1` 2. 减法运算: - 使用`sub`指令将第二个操作数从第一个操作数中减去,并将结果存储在目标寄存器中。 - 例如,要将寄存器`$t0`中的值减去寄存器`$t1`中的值,并将结果存储在`$t2`中,可以使用以下指令:`sub $t2, $t0, $t1` 3. 乘法运算: - 使用`mul`指令将两个操作数相乘,并将结果的低32位存储在目标寄存器中。 - 例如,要将寄存器`$t0`和`$t1`中的值相乘,并将结果的低32位存储在`$t2`中,可以使用以下指令:`mul $t2, $t0, $t1` 4. 除法运算: - 使用`div`指令将第一个操作数除以第二个操作数,并将商存储在目标寄存器中。 - 例如,要将寄存器`$t0`中的值除以寄存器`$t1`中的值,并将商存储在`$t2`中,可以使用以下指令:`div $t0, $t1`,然后使用`mflo`指令将商从特殊寄存器`LO`中移动到`$t2`中:`mflo $t2` 5. 位运算: - 使用相应的位运算指令进行位与(`and`)、位或(`or`)、位异或(`xor`)等操作。 - 例如,要将寄存器`$t0`和`$t1`中的值进行位与,并将结果存储在`$t2`中,可以使用以下指令:`and $t2, $t0, $t1` 以上是用MIPS32汇编语言实现32位算术逻辑运算的一般步骤。如果你有具体的问题或需要更详细的示例,请告诉我。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenmingik

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值