AArch64教程第二章

AArch64教程第二章

在本系列的第一部分,我们做了一个很简单的程序。在本章,我们会继续学习有关AArch64更多的东西

寄存器

计算机仅能对二进制进行操作,所以程序被编码为所谓的二进制码,但是写机器码是很笨拙的,所以,汇编语言应运而生。在汇编中,我们指定程序的指令(和它的操作数)以及数据。指令会告知计算机要做什么(这也就是它们的含义所在)。

CPU是计算机执行程序的部分。一个实现AArch64架构的CPU的指令只能操作CPU内部的数据。在保存CPU内部数据的地方就被称为寄存器(Register)。任何需要被操作的数据首先应放在寄存器中。通常加载的数据来自内存,但也能来自外设。为了能与外部世界通讯,数据也能从寄存器中取出到内存或外设。

在AArch64中有31个通用寄存器。它们被称为通用寄存器是因为它们能够保存任何类型的数据。总的来说,它们能够保存整型值或者地址(我们会在后面的章节说到地址),只要任何能够被编码成64位的值都能保存在寄存器中。这31个寄存器分被被称为x0,x1,…x30.你可以想象为什么使31而不是32,而32更加能体现使2的n次方值。原因是x31寄存器被称为xzr,也就是零寄存器。这是非常特殊的寄存器,并且其用途有限。后面我们将看一些例子怎么样使用这个寄存器。总的来说,所有的寄存器都有一些用途,但是后面一些章节,我们会看到怎么使用它们。

AArch64架构定义了更多的寄存器,它们有更多的用途,我们将在后面的章节揭示它们。

当前64位宽度的寄存器工作是足够的,这也就暗示所有的操作都是在64位的定义域中发生。很多时候,我们不需要这么多位,事实上,大部分的程序值要有32位的数据就足够了(甚至更少)。为了提供32位的处理,我们可以使用Wn来代替Xn表示32位。所以x6寄存器的低32位就是w6。也没必要声明高32位。xzr寄存器的32位等价寄存器是wzr。

这就是为什么我们的程序在第一章是这样的。

mov w0, #2           // w0 ← 2

在C中,主函数main的返回值是int类型。实际上,C程序不会特别指定int类型的宽度(他只是声明能表达值的最小范围),但是为了一些经济上的原因(假定int是在C中最常使用的类型)几乎所有的64位环境(包括AArch64和x86_64)都定义int是一个32位的整型。

在寄存器中处理数据

几乎所有在AArch64中处理的数据有3个操作数。一个目的寄存器和2个源寄存器。例如,我们能够把w3和w4的加法结果放入w5寄存器中:

add w5, w3, w4       // w5 ← w3 + w4

类似

add x5, x3, x4       // x5 ← x3 + x4

但是,注意,我们不能把xn和wn混用。

add w5, w3, x4

否则,会有以下错误提示

add.s:6: Error: operand mismatch -- `add w5,w3,x4'
add.s:6: Info:    did you mean this?
add.s:6: Info:    	add w5,w3,w4
add.s:6: Info:    other valid variant(s):
add.s:6: Info:    	add x5,x3,x4

零寄存器

零寄存器zr(或wzr)是作为源的唯一寄存器。它并不代表一个真正的寄存器,它只是简单地表示“假定一个0在这里作为一个操作数”。

Move

对于1个目的寄存器和2个源寄存器而言,有几个上面所述的不同之处。一个明显的异常是mov指令。它使用一个源寄存器

mov w0, w1    // w0 ← w1

注意,这时一个方便指令,并且它能够用其他指令实现。一种实现方式是把源寄存器加0。一个能够得到上述指令的指令如下

add w0, w1, wzr   // w0 ← w1 + 0

Actually in AArch64 mov is implemented using orr that is an instruction that performs a bitwise or operation using as the first source operand wzr.

事实上,在AArch64内部,mov是通过使用orr实现的,它执行的是位或操作,该操作使用wzr作为第一个操作数。

个人理解:
假设是8位机,设0号寄存器为b0,1号寄存器为b1
1号寄存器的值是0001,0001
orr b0, b1, wzr

  0001, 0001
| 0000, 0000
-------------
  0001, 0001
  
b0的值为0001,0001 

立即数

如果指令的源操作数被限制为寄存器,那就不可能为寄存器加载初始值。这就是为什么一些指令允许立即数。一个立即数是一个整数,该整数能够在指令内部被编码。这样就意味着并不是任何值都有可能被作为立即数被编码,但是很多可以。允许被编码的立即数的范围依赖于指令,但绝大多数允许的范围在 [-4096, 4095] (即12-位)。因为使用了编码,任何在这个范围内被乘以212的数值也是可以被当作立即数。例如,12288和16384也能被当作立即数使用(but not any other number inbetween)。立即数在汇编器中以#打头的方式使用。

mov w0, #2      // w0 ← 2
mov w1, #-2     // w1 ← -2

因为立即数在指令中被编码并且空间大小约束有要求,并且只有一个立即数能够被使用。不同指令下,立即数的位置不同,但是总的来说第二个源操作数一般来说被当作一个立即数。

add w0, w1, #2   // w0 ← w1 + 2
add w0, w1, #-2   // w0 ← w1 + (-2)

下面的指令时不允许的:

add w0, #1, w1   // ERROR: second operand should be an integer register
add w0, #1, #2   // ERROR: second operand should be an integer register.
                 // This case is actually better expressed as
                 //    mov w0, #3

位寄存器作为目的寄存器

当一个指令的目的寄存器是32位寄存器,那么高32位置为0。它们是不被保留的。

一个简单的例子

在此,我们还并不能做什么大动作,但是我们能再我们的程序上做一点小小的事情。当我们程序启动的时候,程序参数的数量在w0中。让我们返回这个数字再加1。

// test.s
.text
.globl main

main:
  add w0, w0, #1   // w0 ← w0 + 1
  ret              // return from main
$ aarch64-linux-gnu-gcc -c test.s
$ aarch64-linux-gnu-gcc -o test test.o
$ ./test ; echo $?
2
$ ./test foo ; echo $?
3
$ ./test foo bar ; echo $?
4

耶!如果你为什么第一种情况返回2而不是1,因为再UNIX中,C的main函数总是接受程序名为第一个参数。

今天就到这里了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值