计算机是怎么跑起来的
体验手工汇编
从程序员的角度看硬件
因为程序的作用就是驱动硬件工作,所以在编写程序之前必须先了解微型计算机的硬件信息。然而真正需要了解的硬件信息只有一下7种,所以没有必要的在编程时还总是盯着详细的电路图看
【CPU信息】
-
CPU的种类
-
时钟信号频率
【内存信息】
- 地址空间
- 每个地址中可以存储多少比特的信息
【I/O信息】
- I/O的种类
- 地址空间-
- 连接着何种周边设备
可以使用哪种机器语言取决于CPU的种类。所谓机器语言就是只用0和1两个二进制数书写的编程语言。即便是相同的机器语言,只要CPU的种类不同,对于它的解释也就不同。
由于微信计算机上的CPU是Z80 CPU,所以就要使用适用于Z80 CPU的机器语言。顾名思义,机器语言就是处理器可以直接理解的编程语言。机器语言有时也叫原生代码。
所谓时钟信号的频率,就是由时钟发生器发送给CPU的电信号的频率。表示时钟发生器的信号频率的单位是MHz(兆赫兹 = 100万回/秒)。时钟信号在0和1两个数字之间来回反复变换的电信号,就像滴答滴答左右摆动的钟摆一样。通常把发出一次滴答的时间称作一个时钟周期。
在机器语言当中,指令执行时所需要的时钟周期数取决于指令的类型。
每个地址都表示着一个内存中的数据存储单元,而这些地址所构成的范围就是内存的地址空间。
连接着I/O的种类,就是指连接着微型计算机和周边设备的I/O的种类只要用CPU控制I/O的寄存器,就可以设定I/O的功能,与周边设备进行数据的输入输出。
所谓I/O的地址空间,是指用于指定I/O寄存器的地址范围。在Z80 PIO上,地址空间为0~3,每一个地址对应一个寄存器。
在内存中,每个地址的功能都一样,既可用于存储指令又可用于存储数据。而I/O则不同,地址编号不同(即寄存器的种类不同),功能也就不同。
机器语言和汇编语言
在机器语言程序中,虽然到处都是0和1的组合,但是每个组合都是有特定含义的指令或数据。可对人来说,如果只看0和1的话,很难判断各个组合都代表什么含义。
于是就有人发明出了一种编程方法,根据表示指令功能的英语单词起一个相似的昵称,并将这个昵称赋予给0和1的组合。这种类似英语单词的昵称叫做“助记符”,使用助记符的编程语言叫作“汇编语言”。
汇编语言的语法十分简单,以至于语法只有一个,即把“标签”“操作码(指令)”和“操作数(指令的对象)”并排写在一行上,仅此而以。
标签的作用是为该行代码对应的内存地址起一个名字。编程时如果总要考虑“这一行的内存地址是什么来着?”就会很不方便,所以在汇编语言中用标签代替地址。用汇编语言编程是可以在任何需要标签的地方“贴上”名称任意的标签。
操作码就是表示“做什么”的指令。因为用助记符表示的指令是英语单词的缩写,比如LD是Load(加载)的缩写,所以多多少少能猜出其中的含义。汇编语言中提供了多少种助记符,CPU就有多少种功能。
操作数表示的是指令的执行的对象。CPU的寄存器、内存地址、I/O地址或者直接给出的数字都可以作为操作数。如果某条指令需要多个操作数,那么它们之间就要用逗号分割。操作数的个数取决于指令的种类。也有不需要操作数的指令,比如用于停止CPU运转的HALT指令。
构成机器语言的是二进制数,而在汇编语言中,则使用十进制数和十六进制数记录数据。
Z80 CPU的寄存器结构
数据的运算是在CPU中进行的,那么在CPU中就应该有存储数据的地方。这种存储数据的地方叫做“寄存器”,但是与I/O的寄存器不同,CPU的寄存器不仅能存储数据,还具备对数据尽心运算的能力。CPU带有什么样的寄存器取决于CPU的种类。Z80 CPU所带的寄存器如下所示。A、B、C、D等字母是寄存器的名字。在汇编语言中,可以将寄存器的名字指定为操作数。
IX、IY、SP、PC这四个寄存器的大小是16比特,其余寄存器的大小都是8比特。寄存器的用途取决于它的类型有的指令只能将特定的寄存器指定为操作数。
举例来说,A寄存器也叫“累加器“,是运算的核心。所以连接到它上面的导线也一定会比其他的寄存器的多。F寄存器也叫做”标志寄存器“,用于存储运算结果的状态,比如是否发生了进位,数字大小的比较结果等。PC寄存器也叫做”程序指针“,存储着指向CPU接下来要执行的指令的地址。PC寄存器的值也会随着滴答滴答的时钟信号自动更新,可以说程序就是依靠不断变化的PC寄存器的值运行起来的。SP寄存器也叫做”栈顶指针“,用于在内存中创建出一块称为”栈“的临时数据存储区域。
追踪程序的运行过程
用汇编语言编写的程序是不能直接运行的,必须先转换成机器语言。机器语言是唯一一种CPU能直接理解的编程语言。1条汇编语言的指令所对应的机器语言由多个字节构成。而且同样是汇编语言中的1条指令,有的对应着1个字节的机器语言,有的指令则对应多个字节的机器语言。汇编语言中的一条指令能转换成多少条机器语言取决于指令的种类以及操作数的个数。
程序像河水一样流动着
程序的流程分为三种
计算机的硬件系统由CPU、I/O和内存三部分构成。内存中存储着程序,也就是指令和数据。CPU配合着由时钟发生器发出的滴答滴答的时钟信号,从内存中读出指令,然后依次对其进行解释和执行。
CPU中有各种各样的各司其职的寄存器。其中有一个被称为pc的寄存器,负责存储内存地址,该地址指向下一条即将执行的指令。每解释执行完一条指令,PC寄存器的值就会自动更新为下一条指令的地址。
PC寄存器的值在大多数情况下只会增加。程序基本上是从内存中的低地址开始,向着高地址顺序执行的。
除了顺序执行外,还有“条件分支”和“循环”。循环则是在程序的特点范围内反复执行若干次的一种流程。条件分支是根据若干个条件的成立与否,在程序的流程中产生若干分支的一种流程。无论规模多大的程序,都是通过把以上的三种流程组合起来实现的。
用流程图表示程序的流程
所谓的流程图,就是表示程序流程的图。有很多专业的程序员在编写程序前,都会通过画流程图或是类似的图来思考程序的流程。
流程图的方便之处在于它并不依赖于特定的编程语言,可以认为编程语言只不过是将流程图上的流程用文字(程序)重现出来罢了。各种编程语言的差异正如一种自然语言中各个地方的方言的差异一样。只要给出了详细的流程图,就可以写出基本相同的程序。
以下图中的符号适用于大部分情况下的流程图的实现。顺序执行只需用直线将矩形框连接起来。条件分支用棱形表示。循环的表示方法是通过条件分支回到前面的处理步骤。这样就能将所有的流程都表示出来了。
表示循环程序块的“帽子”和“短裤”
在计算机硬件上的操作中,循环是通过当满足条件时就返回到之前处理过的步骤来实现的。一旦使用了机器语言或汇编语言所提供的跳转指令,就可以将PC寄存器的值设置为任意的内存地址。如果将它的值设置为之前执行过的步骤所对应的内存地址,那么就构成了循环。因此,在表示循环的时候,仅仅使用带有菱形符号的流程图也就足够了。用机器语言或者汇编语言表示循环时,都是先进行某种比较,再根据比较结果,跳转到之前的地址。
但是,目前使用机器语言与汇编语言的人不多了。程序员使用的都是能够更加高效地编写程序的高级语言,如C语言、BASIC、和Java等。在这些高级语言中,程序员使用“程序块”表示循环而不是跳转指令。所谓“程序块”就是程序中代码的集合。程序中要被循环处理的部分,就是一种程序块。如下图所示的用帽子和短裤符号表示循环的方法就是适用于使用了程序块的高级语言。
用“帽子”和“短裤”表示循环结构没什么问题,也适用于使用高级程序语言编写的程序。但是在直接表示硬件操作的机器语言和汇编语言中,是通过条件分支返回到之前处理过的指令来实现循环的,并没有相当于For或者Next的指令。条件分支本身也是通过跳转指令实现的。根据比较的结果,跳转到之前处理过的步骤就是循环:跳转到之后尚未处理的步骤就是条件分支。
在高级语言中,条件分支也是由程序块表示的。在VBScript中,使用If、ElseIf、Else、End If表示条件分支的程序块。通过关键字就可以形成一个被分成三个区域的程序块(如下代码清单所示)。 如果If关键字后面所写的条件成立,区域(1)中所写的代码就会被执行,形成分支。如果ElseIf后面所写的条件成立,区域(2)中所写的代码就会被执行,形成分支。当这两个条件都不成立时,区域(3)中所写的代码就会被执行,形成分支。高级语言的条件分支代码块,可以用画由菱形符号的流程图表示。
If use = computer Then
MsgBox S & "...平局!" -------------区域(1)
ElseIf computer = (user + 1) Mod 3 Then
MsgBox S & "...玩家获胜!" -------------区域(2)
Wins = Wins + 1
Else
MsgBox S % "...计算机获胜!" -------------区域(3)
End If
结构化程序设计
结构化程序设计就是“为了把程序编写得具备结构性,仅使用顺序指针、条件分支和循环分支表示程序的流程即可,而不再使用跳转指令”。 作为计算机硬件上的行为,无论是条件分支还是循环都必须使用跳转指令实现。但是在VBScript等高级语言中,可以使用if-elseif-else-end if程序块表示条件分支,用for-next程序块表示循环。跳转指令就变得可有可无。但是,在很多高级语言中,还是提供了与机器语言中跳转指令相当的语句,例如VBScript中的GoTo语句。因为跳转指令所带来的危害不小,会使程序陷入复杂状态,所以尽可能避免使用跳转语句。
特殊的程序流程-中断处理
中断处理是指计算机使程序的流程突然跳转到程序中的特定地方,这样的地方被称为中断处理例程或是中断处理程序,而这种跳转是通过CPU所具备的硬件功能实现的。就好比我们正在书桌前处理文件,这时突然来电话了,我们就得停下手头的工作来接电话,接完后又回到之前的工作。像这样由于外部的原因使正常的流程中断,中断后在返回之前流程的过程就是中段处理流程。
特殊的程序流程-事件驱动
通常把用户在应用程序中点击鼠标或者敲击键盘这样的操作称做“事件”。负责检测事件的是Windows。Windows通过调用应用程序的WndProc()函数通知应用程序事件的发生。而应用程序则根据事件的类型做出相应的处理。这种机制就是事件驱动。可以说事件驱动也是一种特殊的条件分支,它以从Windows收来的通知为条件,根据通知的内容决定程序的下一步流程。
要实现事件驱动,就必须把应用程序中的WndProc()函数的起始内存地址告诉Windows。这一步将在应用程序WinMain()中作为初始化处理被执行。