电子计算机,顾名思义,就是计算的机器。因此,学习汇编语言,就不可避免地要和数字打交道。在这个过程中,我们要用到三种数制:十进制(这是我们再熟悉不过的)、二进制和十六进制。本章的目标是:
1.熟悉后两种数制,了解这两种数制的计数特点;
2.能够在这三种数制之间熟练地进行转换,特别是在看到一个二进制数时,能够口算出它对应的十六进制数,反之亦然;
3.对于0~15之间的任何一个十进制数,能够立即说出它对应的二进制数和十六进制数。
在《穿越计算机的迷雾》那本书里我们已经知道,计算机也是一台机器,唯一不同的地方在于它能计算数学题,且具有逻辑判断能力。
与此同时,我们也已经在那本书里学到,机器在做数学题的时候,也面临着一个如何表示数字的问题,比如你采用什么办法来将加数和被加数送到机器里。
同样是在那本书里,我们揭晓了答案,那就是用高、低两种电平的组合来表示数字。如图1-1所示,参与计算的数字通过电线送往计算机器,高电平被认为是“1”,低电平被认为是“0”,这样就形成了一个序列“11111010”,这就是一个二进制数,在数值上等于我们所熟知的二百五,换句话说,等于十进制数250。
图1-1 在计算机里,二进制数字对应着高、低电平的组合
从数学的角度来看,二进制计数法是现代主流计算机的基础。一方面,它简化了硬件设计,因为它只有两个符号“0”和“1”,要得到它们,用最少的电路元件来接通或者关断电路就行了;另一方面,二进制数与我们熟悉的十进制数之间有着一对一的关系,任何一个十进制数都对应着一个二进制数,不管它有多大。比如,十进制数5,它所对应的二进制数是101,而十进制数5785478965147则对应着一长串“0”和“1”的组合,即1010100001100001001011010110010011110011011。
组成二进制数的每个数位,称为一个比特(bit),而一个二进制数也可以看成一个比特串。很明显,它的数值越大,这个比特串就越长,这是二进制计数法不好的一面。
每种计数法都有自己的符号(数符)。比如,十进制有0、1、2、3、4、5、6、7、8、9这10个符号;二进制呢,则只有0、1这两个符号。这些数字符号的个数称为基数。也就是说,十进制有10个基数,而二进制只有两个。
二进制和十进制都是进位计数法。进位计数法的一个特点是,符号的值和它在这个数中所处的位置有关。比如,十进制数356,数字6处在个位上,所以是“6个”;5处在十位上,所以是“50”;3处在百位上,所以是“300”,即
百位3、十位5、个位6=3×10 2 +5×10 1 +6×10 0 =356
这就是说,由于所处的位置不同,每个数位都有一个不同的放大倍数,这称为“权”。每个数位的权是这样计算的(这里仅讨论整数):从右往左开始,以基数为底,指数从0开始递增的幂。正如上面的公式所清楚表明的那样,“6”在最右边,所以它的权是以10为底、指数为0的幂10 0 ;而3呢,它的权则是以10为底、指数为2的幂10 2 。
上面的算式是把 十进制 数“翻译”成 十进制 数。从十进制数又算回到十进制数,这看起来有些可笑,注意这个公式是可以推广的,可以用它来将二进制数转换成十进制数。
比如一个二进制数10110001,它的基数是2,所以要这样来计算与它等值的十进制数:
10110001B=1×2 7 +0×2 6 +1×2 5 +1×2 4 +0×2 3 +0×2 2 +0×2 1 +1×2 0 =177D
在上面的公式里,10110001B里的“B”表示这是一个二进制数,“D”则表示177是一个十进制数。“B”和“D”分别是英语单词Binary和Decimal的首字母,这两个单词分别表示二进位和十进位的意思。
◆ 检测点1.1
将下列二进制数转换成十进制数:
1101、1111、1001110、11111111、10000000、1101101100011011
为了将一个十进制数转换成二进制数,可以采用将它不停地除以二进制的基数2,直到商为0,然后将每一步得到的余数串起来即可。如图1-2所示,如果要将十进制数26转换成二进制数11010,那么可采用如下方法:
第1步,将26除以2,商为13,余数为0;
第2步,用13除以2,商为6,余数为1;
第3步,用6除以2,商为3,余数为0;
图1-2 将十进制数26转换成二进制数
第4步,用3除以2,商为1,余数为1;
第5步,用1除以2,商为0,余数为1,结束。
然后,从下往上,将每一步得到的余数串起来,从左往右书写,就是我们所要转换的二进制数。
◆ 检测点1.2
将下列十进制数转换成二进制数:
8、10、12、15、25、64、100、255、1000、65535、1048576
二进制数和计算机电路有着近乎直观的联系。电路的状态,可以用二进制数来直观地描述,而一个二进制数,也容易使我们仿佛观察到了每根电线上的电平变化。所以,我们才形象地说,二进制是计算机的官方语言。
即使在平时的学习和研究中,使用二进制也是必需的。一个数字电路输入什么,输出什么,电路的状态变了,是哪一位发生了变化,研究这些,肯定要精确到每比特。这个时候,采用二进制是最直观的。
但是,二进制也有它的缺点。眼下看来,它最主要的缺点就是写起来太长,一点也不方便。为此,人们发明了十六进制计数法。至于为什么要发明另一套计数方法,而不是依旧采用我们熟悉的十进制计数法,下面就要为大家解释。
一旦知道二进制有两个数符“0”和“1”,十进制有十个数符“0”到“9”,那么我们就会很自然地认为十六进制一定有16个数符。
一点没错,完全正确。这16个数符分别是0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F。
你可能会觉得惊讶,字母怎么可以当作数字来用?这样的话,那些熟悉的英语单词,像Face(脸)、Bad(坏的)、Bed(床)就都成了数。
这又有什么奇怪的?你觉得“0”“5”“9”是数字,而“A”“B”不是数字,这是因为你从小习惯了这种做法。
对于自然数里的前10个,十进制和十六进制的表示方法是一致的。但是,9之后的数,两者的表示方法就大相径庭了,如表1-1所示。
表1-1 部分十进制数和十六进制数对照表
很显然,一旦某个数位增加到9之后,下一次,它将变成A,而不是向前进位,因为这里是逢16才进位的。进位只发生在某个数位原先是F的情况下,比如1F,它加1后将会变成20。
要把一个十六进制数转换成我们熟悉的十进制数,可以采用和前面一样的方法。只不过,在计算各个数位的权时,幂的底数是16。将十六进制数125转换成十进制数的方法如下:
125H=1×16 2 +2×16 1 +5×16 0 =293D
在上式中,125后面的“H”用于表明这是一个十六进制数,它是英语单词Hexadecimal的首字母,这个单词的意思是十六进制。
◆ 检测点1.3
将下列十六进制数转换成十进制数:
8、A、B、C、D、E、F、10、1F、6CD、3FE、FFC、FFFF
如图1-3所示,相应地,要把一个十进制数转换成十六进制数,则可以采取不停地除以16并取其余数的策略。
第1次,将293除以16,商为18,余5;
第2次,用18除以16,商为1,余2;
第3次,再用1除以16,商为0,余1,结束。
然后,从下往上,将每次的余数1、2、5列出来,得到125,这就是所要的结果。
图1-3 将十进制数293转换成十六进制数
◆ 检测点1.4
将下列十进制数转换成十六进制数:
8、10、12、15、25、64、100、255、1000、65535、1048576
为什么我们要发明十六进制计数法?为什么我们要学习它?
提出这样的问题,在我看来很有趣,也很有意义,但似乎从来没有人在书上正面回答过。这样一来,学子们只能在掌握了十六进制若干年之后,在某一天自己恍然大悟。
为了搞清楚这个问题,我们不妨来列张表(见表1-2),看看十进制数、二进制数和十六进制数之间,都有些什么有趣的规律和特点。
表1-2 部分十进制数、二进制数和十六进制数对照表
在上面这张表里(见表1-2),每个二进制数在排版的时候,都经过了“艺术加工”,全都以4比特为一组的形式出现。不足4比特的,前面都额外加了“0”,比如10,被写成0010的形式。就像十进制数一样,在一个二进制数的前面加多少个零,都不会改变它的值。
注意观察这张表并开动脑筋,4比特的二进制数,可以表示的数是0000~1111,也就是十进制的0~15,这正好对应于十六进制的0~F。
在这个时候,如果将它们都各自加1,那么,下一个二进制数是0001 0000,与此同时,它对应的十六进制数则是10,你会发现,它们有如图1-4(a)所示的奇妙对应关系。
图1-4 十六进制的每一位与二进制数每4比特为一组的对应关系
再比如图1-4(b)中的二进制数1100 0011,它与等值的十六进制数C3也有着相同的对应关系。
也就是说,如果将一个二进制数从右往左,分成4比特为一组的形式,分别将每一组的值转换成十六进制数,就可以得到这个二进制数所对应的十六进制数。
这样一来,如果我们稍加努力,将0~F这16个数所对应的二进制数背熟,并能换算自如的话,那么,当我们看到一个十六进制数3F8时,我们就知道,因为3对应的二进制数为0011,F对应的二进制数是1111,8对应的二进制数是1000,所以3F8H=0011 1111 1000B。
同理,如果一个二进制数是1101 0010 0101 0001,那么,将它们按4比特为一组,分别换算成十六进制数,就得到了D251。
正如前面所说的,从事计算机的学习和研究(包括咱们马上就要进行的汇编语言程序设计),不可避免地要与二进制数打交道,而且有时还必须针对其中某些比特进行特殊处理。这个时候,如果想保留二进制数的直观性,同时还要求写起来简短,十六进制数是最好的选择。
◆ 检测点1.5
1.将下列十六进制数转换成二进制数:
3、A、C、F、20、3F、2FE、FFFF、9FC05D、7CCFFEFF
2.快速说出以下十进制数所对应的二进制数和十六进制数:
1、3、5、7、9、11、13、15、0、2、4、6、8、10、12、14
和十进制数一样,二进制数和十六进制数也可以进行加、减、乘、除运算。比如,两个十六进制数F和A相乘,结果是十六进制数96。从十进制的角度来看这个计算过程,就是两个十进制数15和10相乘,结果为150。
在学习汇编语言程序设计的过程中,出于解决实际问题的需要,经常要在编写程序时做一些计算工作。十进制就不说了,我们都很熟悉,计算起来驾轻就熟。但是,如果是几个二进制数进行加减乘除,或者几个十六进制数加减乘除,就很困难了。想想看,为了做十进制乘法,我们要背九九乘法口诀。而十六进制有16个基数,它的乘法口诀就更多了。
这本书的目的不是教会你十六进制四则运算的方法和步骤,不是这样的。相反,我希望你能借助一些工具来快速得到计算结果,从而把更多的精力放到学习汇编语言上。
不是所有知识都应当放在脑子里,要善于利用工具!
为了将较大的数转换成不同的数制,或者进行某种数制的四则运算,可以使用Windows计算器。这是一个小软件,每个版本的Windows操作系统都有,你应该很熟悉,其界面如图1-5所示。注意,如果该程序运行后的界面与此不同,则可以通过选择菜单“查看”→“程序员”进行更改。
计算器软件的使用方法并不复杂,只需要稍加练习即可掌握。比如,选择单选钮“十六进制”,然后输入一个十六进制数。此时,如果你再选择单选钮“十进制”,则刚才输入的内容就会立即变成十进制的形式,这就是进行数制转换的一个例子。
◆ 检测点1.6
1.用计算器程序将FFCH转换成十进制数和二进制数;
2.用计算器程序计算FFCH乘以27C0H的结果,并转换成二进制数。
图1-5 Windows计算器
1.口算:
5H=___D 12D=___H 0FH=___D=___B
0CH=___D=___B 0AH=___D=___B 8D=___H=___B
0BH=___D=___B 0EH=___D=___B 10H=___D=___B
2.口算:
10010B=___H 15H=___B 8FH=___B 200H=___B 111111111B=___H