什么是机器语言?
机器语言就是由二进制数字构成的程序,CPU 可以直接对其解释、执行。
汇编语言、C 语言、JAVA、BASIC 等编程语言编写的程序,也都需要先转换成机器语言才能被执行。机器语言有时也叫作“原生代码”(Native Code)。
本篇文章我们就来学习一下如何编写汇编语言,汇编语言是低级语言,所以它是最接近机器语言的编程语言。
学习汇编语言,能让你更深刻的理解计算机的运行机制和原理,使你犹如拨云见日, 找到长期困惑着自己的问题的答案,不仅能因“我能看懂程序了”而获得成就感,更能因发现“计算机原来很简单啊”而信心倍增。
使用汇编语言编写一段程序然后让计算机运行你编写的程序,你就知道计算机是如何把汇编语言转换成机器语言执行的。
你可能会问,我们为什么不直接使用机器语言编写程序呢?原因就是机器语言是一堆的0和1的组合,但是每个组合都是有特定的含义的指令或数据,所以对人来说,如果只看0和1的话很难判断出各个组合都表示什么。
简单地说,就是机器语言不适合人类书写和阅读。
于是就有人发明了汇编语言,可以用一些英文单词或者英文简写来表示指令功能,这种类似的英语单词叫作“助记符”,使用助记符的编程语言叫作“汇编语言”。
无论是使用机器语言还是汇编语言,所实现的功能都是一样的, 区别只在于程序是用数字表示,还是用助记符表示。
也就是说,如果理解了汇编语言,也就理解了机器语言,更进一步也就理解了计算机 的原始的工作方式。
汇编语言的语法十分简单,以至于语法只有一个,即把“标签” “操作码(指令)”和“操作数(指令的对象)”并排写在一行上,仅此而已。
如下是一段汇编语言:
标签 操作码 操作数
LD A, 207
OUT (2), A
LD A, 255
OUT (2), A
LD A, 207
OUT (3), A
LD A, 0
OUT (3), A
LOOP: IN A, (0)
OUT (1), A
JP LOOP
标签的作用是为该行代码对应的内存地址起一个名字。
编程时如果总要考虑这一行的内存地址是什么?就会很不方便,所以在汇编语言中用标签来代替地址。用汇编语言编程时可以在任何需要标签的地方贴上名称任意的标签。
在上述代码程序中,使用了名称为“LOOP:”的标签,操作码就是表示“做什么”的指令。
因为用助记符表示的指令是英语单词的缩写,比如 LD 是 Load(加载)的缩写,所以多多少少能猜出其中的含义。汇编语言中提供了多少种助记符,CPU 就有多少种功能。
比如Z80 CPU 的指令全部加起来有 70 条左右。
CPU 的寄存器、内存地址、I/O 地址或者直接给出的数字都可以作为操作数。
如果某条指令需要多个操作数,那么它们之间就要用逗号分割。操作数的个数取决于指令的种类。也有不需要操作数的指令,比如用于停止 CPU 运转的 HALT 指令。
汇编语言的语法和英语祈使句的语法很像。对比英语的祈使句 Give me money 和汇编语言的语句,就可以看出在英语的祈使句中,一 开头放置了一个表示“做什么”的动词,这个动词就相当于汇编语言中的操作码。
在动词后面放置了一个表示“动作作用到什么上”的宾语, 这个宾语就相当于汇编语言中的操作数。
因为程序的作用是向 CPU 发出指令,而且编程语言又是由说英语的人发明的,所以编程语言与英语祈使句类似也就不足为奇了。
构成机器语言的是二进制数,而在汇编语言中,则使用十进制数和十六进制数记录数据。若仅仅写出123这样的数字,表示的就是十进制数;而像123H这样在数字末尾加上了一个H(H 表示 Hexadecimal, 即十六进制数),表示的就是十六进制数。
这里先把主要的指令列在如下表中,请大家粗略地浏览一下。
在浏览的过程中请注意这些指令的分类,按功能这些指令可以分成运算、与内存的输入输出和 与 I/O 的输入输出三类。
这是因为计算机能做的事也只有输入、运算、输出这三种了。 操作数表示的是指令执行的对象。
指令的种类 |
助记符 |
功能 |
运算指令
|
ADD A, num ADD A, reg SUB num SUB reg INC reg DEC reg AND num AND reg OR num OR reg XOR num XOR reg SLA reg SRA reg SRL reg CP num CP reg |
把数值 num 加到寄存器 A 的值上 把寄存器 reg 的值加到寄存器 A 的值上 从寄存器 A 的值中减去数值 num 从寄存器 A 的值中减去寄存器 reg 的值 将寄存器 reg 的值加 1 将寄存器 reg 的值减 1 计算寄存器 A 的值和数值 num 的逻辑积 计算寄存器 A 的值和寄存器 reg 的值的逻辑积 计算寄存器 A 的值和数值 num 的逻辑和 计算寄存器 A 的值和寄存器 reg 的值的的逻辑和 计算寄存器 A 的值和数值 num 的逻辑异或 计算寄存器 A 的值和寄存器 reg 的值的逻辑异或 对寄存器 reg 的值进行算数左移运算 对寄存器 reg 的值进行算数右移运算 对寄存器 reg 的值进行逻辑右移运算 比较寄存器 A 的值和数值 num 的大小 比较寄存器 A 的值和寄存器 reg 的值的大小 |
内存与 CPU 之间的输入 输出指令 |
LD reg, num LD reg1, reg2 LD (num), reg LD (reg), reg
PUSH reg POP reg |
把数值 num 写入到寄存器 reg 中 把寄存器 reg2 的值写入到寄存器 reg1 中 把寄存器 reg 的值写入到地址 num 上 把寄存器 reg2 的值写入到存放在寄存器 reg1 中的地址上 把寄存器 reg 的值写入到栈中 把由栈顶读出的数据存放到寄存器 reg 中 |
I/O 与 CPU 之间的输入 输出指令 |
IN A, (num) IN reg, (C)
OUT (num), A OUT (C), reg |
从地址 num 中读出数据,存放到寄存器 A 中 从存储在寄存器 C 中的地址上读出数据,存放到 寄存器 reg 中 把寄存器 A 的值写入到地址 num 上 把寄存器 reg 的值写入到存储在寄存器 C 中的地址上 |
程序流程控 制指令 |
JP num |
使程序的流程跳转到地址 num 上,接下来从那个 地址上的指令开始执行 |
CALL num RET HALT |
调用存放在地址 num 上的子例程 从 子例程中返回 中止 CPU 的运行 |
欢迎关注我,学习更多计算机知识!