在正式介绍汇编语言之前,我会先用几篇文章讲一些数学基础和硬件基础。如果读者已经具备了一定的知识基础,可以直接跳过这些文章去汇编语言部分。
二进制,八进制与十六进制
在计算机底层的软件层面,我们通常采用二进制,八进制或十六进制来记录数字,其中最常用的是十六进制。所谓
n
n
n进制,就是从0开始数,逢
n
n
n进1. 比如说二进制,就是从0开始数,到1,然后到2的时候进1变成10. 八进制也是类似,但是到了十六进制就犯了难,我们的数字只有0到9这十个,并不能表示出16个呀,于是,我们默认使用了a到f这六个字母来分别表示10到15这六个数。也就是说,十进制数10对应的十六进制数是a, 十进制数26对应的十六进制数是1a. 在计算机底层,通常用0x
开头表示十六进制,用0
开头表示八进制,而没有前缀来表示十进制。因此,比如说以下的汇编代码(并不需要理解实际含义)
movq $0x1a, %rax
与
movq $26, %rax
相同。
十进制数与十六进制数的转化可以在搜索引擎上找到,这里不再赘述。而八进制,十六进制数与二进制数的转换则十分简单。一个八进制数的一位代表一个二进制数的三位,比如说八进制数的一位5
就代表二进制数的三位011
; 同理,一个十六进制数的一位就代表二进制数的四位。因此,十六进制数0x2000001
就代表二进制数0010000000000000000000000001
.
我们知道,之所以使用二进制数,是因为计算机底层采用高电平/低电平这种方法来表示数。那么,我们为什么要使用八进制、十六进制呢?我们知道,如今的计算机大多采用64位系统,意思是说,任何一个地址都是一个64位二进制数。那么,如果我们只采用二进制来表示一个地址,那么得有64个0
或者1
, 这不仅让我们看花眼了,而且也极大的浪费了电脑的显示资源。而刚才讲到的十六进制数则帮我们解决了这个问题。我们知道,十六进制数的一位对应二进制数的4位。因此,一个
n
n
n位二进制数,只需要
⌈
n
4
⌉
\lceil\frac{n}{4}\rceil
⌈4n⌉位十六进制数即可。也就是说,我们要表示64位的地址,只需要16位十六进制数即可。
补码
进制问题解决了在计算机底层软件中数的表示问题,接下来还需要解决的是记录问题,也就是说,如何把数实际存储在64位寄存器中。我们想要解决两个问题:
- 如何记录负数
- 可以使用加法器计算减法么
天才般的先行者,使用了补码来一举解决了这两个问题。
想要解决第二个问题,一个想法自然出现了,既然 a − b = a + ( − b ) a-b=a+(-b) a−b=a+(−b), 那可以在加法器中输入一个正数和一个负数来实现减法呀。
然而,我们知道,在计算机中,一个存储单位存储的数据大小是有上限的。比如说在64位CPU中,每个寄存器有64位,因此可以存储64位二进制数。因此,在CPU的加法器中,实际上使用了模 2 64 2^{64} 264加法。也就是说,加法器做的,就是对于输入的两个64位二进制数 a a a和 b b b, 输出64位二进制数 ( a + b )   m o d   2 64 (a+b)\bmod{2^{64}} (a+b)mod264.
因此,我们只有找到合适的将负数记录成64位二进制数的方法,才能将加法器转化为减法器。
注意到
a
−
b
≡
a
+
(
2
64
−
b
)
(
m
o
d
2
64
)
a-b\equiv a+\left(2^{64}-b\right)\pmod{2^{64}}
a−b≡a+(264−b)(mod264)
而由于
b
b
b是64位二进制数,因此,
2
64
−
b
2^{64}-b
264−b必然是一个正数,而正数的记录方法我们是知道的。因此,我们可以使用
2
64
−
b
2^{64}-b
264−b来记录
−
b
-b
−b, 其参与的减法就可以变成相应的加法。
但是,还有一个细节需要注意。比如说,我们想要记录的二进制数是0xfffffffffffffffe
, 那么根据刚刚讨论的,我们可以将其记录为0x1
. 这就出现了问题,如何区分0x1
和0xfffffffffffffffe
呢?我们采用这种方法只是为了方便减法,并不打算将正数和负数混同啊。
因此,在实际操作中,当出现负数时,能够允许的负数的绝对值最大值是
2
63
2^{63}
263. 换句话说,其记录值最高位0
表示正数,1
表示负数。这种记录方法叫做补码。也就是说,对于小于
2
63
2^{63}
263的正数,采用其二进制表示为其实际记录;对于不低于
−
2
63
-2^{63}
−263的负数,将其加上
2
64
2^{64}
264后的正数的二进制表示为其实际记录。如果采用补码,那么可以表示
−
2
63
∼
2
63
−
1
-2^{63}\sim2^{63}-1
−263∼263−1的整数。因此,采用补码记录的数称为有符号整数。反之,如果直接使用其二进制表示为其记录的话,那么只能表示
0
∼
2
64
−
1
0\sim 2^{64}-1
0∼264−1的整数。因此,这种数的记录形式称为无符号整数。
逻辑运算
除了加减乘除以外,二进制数还有独特的运算——逻辑运算。分别是与(and), 或(or), 非(not)和异或(xor). 与或非大家都很熟悉了,异或就是当且仅当两个操作数不同时输出1
, 相同时输出0
.
可以在哪看到这系列文章
上一篇文章:macOS上的汇编入门(一)——引言
下一篇文章:macOS上的汇编入门(三)——硬件基础