第1,2章
1. 从问题描述到电子运转
用计算机来解决问题,总的来说分为如下几个层次
问题——算法——语言——机器(ISA)结构——微结构——电路——器件
1.1 问题
描述问题的时候,我们最常用的就是自然语言,人们可以很容易的知道其中的意思,但计算机就未必,因为自然语言包含太多的二义性(对于计算机来说这会让它茫然不知所措)
1.2 算法
从问题的提出开始,向下转换的第一步就是将问题的自然语言描述转换为算法描述。算法描述有如下特点:
- 确定性
- 可计算性
- 有限性
1.3 程序语言
下一步将算法转换为程序,即用编程语言描述。编程语言属于“机械语言”,与自然语言不同,被设计成严格的顺序方式,以便让计算机顺序的执行指令序列(即不存在二义性问题)
计算机语言分为高级语言和低级语言两类,前者包括C/C++,java,python等,后者则包括各种汇编语言,通常一种低级语言只对应一种计算机,我们称之为“某某机器的汇编语言”
1.4 机器结构(指令集结构)
再下一步就是将程序转换成特定计算机的指令集,指令集结构(Instruction Set Architecture, ISA)是程序和计算机硬件之间接口的一个完整定义。
ISA定义的内容包括:
- 计算机可以执行的指令集合,即计算机所能执行的操作,以及每个操作所需数据是什么,即操作数
- 可接受的操作数表达方式,即数据类型
- 获取操作数的机制,定位各种操作数的不同方法,即寻址模式
或者换一种理解方式:指令集相当于对一段固定宽度的01序列规定了识别的规则
比如:对于一个16位的二进制序列,我们可以规定前4位是操作码,从0000到1111可以规定16种不同的操作,同时我们规定其中的0001是ADD操作(加法),然后可以再规定第5位到第10位(包括5和10)是第一个操作数,第11位到第16位(包括11和16)是第二个操作数
那么对于一个16位的二进制序列如0001000100000010,就可以解读如下
0001(表示ADD)000100(即4)000010(即2)——即4+2
当然,如上的例子中并没有标识数据类型,寻址模式等,仅仅只是以简单的方式帮助理解指令集的概念而已
不用的ISA定义的操作类型,数据类型和寻址模式的数目都是不同的,设计时还需要考虑计算机内存的大小及每个存储单元的宽度(即能容纳的0和1的数目)
许多ISA一直延续至今,典型的例子就是x86
将高级语言(如C语言)翻译为ISA指令(如x86)的过程,通常是由一个被称为“编译器”的程序来实现的。例如,将C语言翻译成x86 ISA时,需要一个“x86的C编译器”,就是说,针对不同的高级语言和目标计算机组合,需要一个对应的编译器
而将特定计算机的汇编语言程序翻译为其ISA的过程,则是由汇编器来完成的
需要注意的是,汇编语言并不能和指令集等同
- 指令集是硬件层面的抽象概念,描述了处理器能执行的基本操作。它是对计算机硬件的抽象,是处理器架构的一部分。
- 汇编语言则是一种低级编程语言,用来代表和操作指令集。它为程序员提供了与处理器指令集更直接的接口,比机器语言更容易理解和编写。
- 每种处理器架构都有自己的指令集,如x86、ARM、MIPS等。而针对不同的指令集,都会有对应的汇编语言。
换句话说,前文的加法指令,我们可以直接用ADD来代表0001,用4来代表000100,用2来代表000010
那么这句话就可以写成ADD 4 2
用相对易于解读的语言来描述二进制序列,这就是汇编语言
而对于不同的指令集来说,由于制定的规则不同,那么想用汇编语言去描述二进制序列也就不一样,这就是针对不同的指令集,都会有对应的汇编语言的原因
相应的,基于指令集的规则,每一条汇编语言的指令都可以直接转换为二进制序列,供计算机识别解读
从某种意义上说,01序列是最靠近底层(即计算机)的语言(让我们姑且这样说),汇编语言则是初步向着便于人类理解的方向靠近了一步,尽管被称为低级语言,但这也是和高级语言相对应的,高级语言则比汇编更靠近了人类一步
1.5 微结构
再下一步就是将ISA转换成对应的实现,实现的具体组织被称为微结构。例如,近年来许多处理器都实现了x86这种ISA结构,但每个处理器的实现方法不同,即有自己的微结构
1.6逻辑电路
微结构最终是由一组简单的逻辑电路实现的,有多种实现方法可选择,各种方法之间存在着性能(运算速度)和成本上的差异
1.7 器件
再到最后,每个基本的逻辑电路,都是按照特定的器件技术来实现的,不同的电子元器件有不同的电学特性,不过这就不属于计算机的范畴了
2. bit、数据类型及其计算
计算机识别的是二进制(01)序列,一般通过电路的高低电平来实现
在存储中,采用补码来表示整数,ASCII码表示字符
进制转换(十转二为例)
整数部分除2取余,自下向上取,小数部分乘2取整,自上向下取
算术运算
同位数的两个数正常加即可,A-B=A+(-B)
不同位的两个数需要扩展,整数前补0,负数前补1
注意溢出问题
逻辑运算
- 与:全1出1,有0出0
- 或:有1出1,全0出0
- 非:1转0,0转1
- 异或:相同为0,不同为1
位矢量:假设存在n个单元,我们可以用n位二进制数表示这n个单元,当某个单元空闲时,将相应的bit清0,忙碌时置1。我们称这个二进制数为位矢量
屏蔽字:方便我们只与一系列bit中的一部分bit打交道,比如AND(与运算)了一个111101111和目标数,就是将目标数的第5位清零
浮点数
大部分指令集都定义了一种或多种浮点数类型。其中之一通常被称作”float“类型,由32bit组成,各bit定义如下:
符号:1bit,表示正负
数值范围:8bit,指数[1, 254]
数值精度:23bit,尾数
该格式是IEEE浮点运算标准的一部分
尾数部分是被正则化的,即小数点左边有且仅有1位非0数字 (而在二进制表示下,这个数字只能是1,于是这个必然的1被省略了,以提高精度)
对于指数部分,则是无符号整数,但是上文的范围是[1, 254],对于00000000和11111111并未做解释(事实上这本来也是不要求的,感兴趣可以自行检索)
对于那254个取值,则需要作如下解读:该值减去127所得的值才是真正的指数值,例如指数部分表示为10000111(无符号数135)时,实际的指数值为135-127=8,这使得指数可以为正也可以为负,相应的也就可以表示很大或很小的数了
最高的一位则是符号位
16进制计数法:较为简便地表示二进制,比如0011110101101110可以表示为0011(3)1101(D)0110(6)1110(E)