体验手工汇编
为了体验手工汇编,我们需要先了解计算机基本硬件信息。
从程序员角度看硬件
需要重点了解的不多:
- CPU
-
- CPU种类
- 时钟信号频率
- 内存信息
-
- 地址空间
- 每个地址可以存储多少比特信息
- I/O信息
-
- I/O种类
- 地址空间
- 连接着什么周边设备
下面挨个介绍
CPU种类 | 决定机器语言的种类 |
时钟信号频率 | 在0/1之间反复变换* |
地址 | 内存中的数据储存单元 |
地址空间 | 所有地址所构成的范围 |
I/O种类 | 连接着微型计算机和周边设备的 I/O的种类 |
I/O地址空间 | 原理同上地址空间 |
机器语言和汇编语言
机器语言是计算机能够直接理解的语言,由二进制数字组成;汇编语言是一种用助记符表示机器语言的编程语言,更易于理解和编写。
地址 程序 00000000 00111110 00000001 11001111 00000010 11010011 00000011 00000010 00000100 00111110 00000101 11111111 00000110 11010011 00000111 00000010 00001000 00111110 00001001 11001111 00001010 11010011 00001011 00000011 00001100 00111110 00001101 00000000 00001110 11010011 00001111 00000011 00010000 11011011 00010001 00000000 00010010 11010011 00010011 00000001 00010100 11000011 00010101 00010000 00010110 00000000 // 过拨动指拨开关控制 LED 的亮或灭 // 原理:把数据输入 CPU,CPU 再把这些数据输出到 LED // 数据由指拨开关输入
CPU 会从 0 号地址开始顺序执行这段程序。
像上面这样由0和1组成的程序,是给机器看的,但是人看不懂,于是牛b人就发明了一个编程方法。
把特定的01组合,起个名字,用这些名字编程,就叫汇编语言。
汇编语言和机器语言唯一的区别就是,一个是给人看的 ,一个是给机器看的。
上面的机器码转化成汇编如下:
标签 操作码 操作数 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
标签
标签的作用是:为代码中的特定位置或指令赋予一个有意义的名称。
因为机器语言是由二进制数字组成的,难以直接理解和记忆,而汇编语言使用助记符来表示指令,使得代码更具可读性。
当程序需要跳转到特定的代码位置时,使用标签可以更方便地指定目标位置,而不需要记住具体的内存地址。
例如,在汇编语言中,可以使用标签来标记一个循环的开始或结束位置,或者标记一个函数的入口点。
这样,在代码中就可以使用标签来代替具体的内存地址进行跳转或引用,提高了代码的可读性和可维护性。
总之,为了易于理解和编写,方便了控制和管理。
操作码
操作码就是表示做什么的指令。
按功能这些指令可以分为运算、与内存的输入输出、与I/O的输入输出。
下面这个表简单看一下就行。
指令的种类 | 助记符 | 功能 |
---|---|---|
运算指令 | ADD A,num | 把数值 num 加到寄存器 A 的值上 |
ADD A,reg | 把寄存器 reg 的值加到寄存器 A 的值上 | |
SUB num | 从寄存器 A 的值中减去数值 num | |
SUB reg | 从寄存器 A 的值中减去寄存器 reg 的值 | |
INC reg | 将寄存器 reg 的值加 1 | |
DEC reg | 将寄存器 reg 的值减 1 | |
AND num | 计算寄存器 A 的值和数值 num 的逻辑积 | |
AND reg | 计算寄存器 A 的值和寄存器 reg 的值的逻辑积 | |
OR num | 计算寄存器 A 的值和数值 num 的逻辑和 | |
OR reg | 计算寄存器 A 的值和寄存器 reg 的值的逻辑和 | |
XOR num | 计算寄存器 A 的值和数值 num 的逻辑异或 | |
XOR reg | 计算寄存器 A 的值和寄存器 reg 的值的逻辑异或 | |
SLA reg | 对寄存器 reg 的值进行算数左移运算 | |
SRA reg | 对寄存器 reg 的值进行算数右移运算 | |
SRL reg | 对寄存器 reg 的值进行逻辑右移运算 | |
CP num | 比较寄存器 A 的值和数值 num 的大小 | |
CP reg | 比较寄存器 A 的值和寄存器 reg 的值的大小 | |
内存与 CPU 之间的输入输出指令 | LD reg,num | 把数值 num 写入到寄存器 reg 中 |
LD reg1,reg2 | 把寄存器 reg2 的值写入到寄存器 reg1 中 | |
LD(num),reg | 把寄存器 reg 的值写入到地址 num 上 | |
LD(reg),reg | 把寄存器 reg2 的值写入到存放在寄存器 reg1 中的地址上 | |
PUSH reg | 把寄存器 reg 的值写入到栈中 | |
POP reg | 把由栈顶读出的数据存放到寄存器 reg 中 | |
I/O 与 CPU 之间的输入输出指令 | IN A,(num) | 从地址 num 中读出数据,存放到寄存器 A 中 |
OUT(num),A | 把寄存器 A 的值写入到地址 num 上 | |
程序流程控制指令 | JP num | 使程序的流程跳转到地址 num 上,接下来从那个地址上的指令开始执行 |
CALL num | 调用存放在地址 num 上的子例程 | |
RET | 从子例程中返回 | |
HALT | 中止 CPU 的运行 |
操作数
操作数就是指令执行的对象了,就是要对谁进行处理。
总结
汇编语言和英语祈使句的很像。
对比英语的祈使句Give me money 和汇编语言的语句,
可以看出在英语的祈使句中,一开头放置了一个表示“做什么”的动词,这个动词就相当于汇编语言中的操作码。
在动词后面放置了一个表示“动作作用到什么上”的宾语,这个宾语就相当于汇编语言中的操作数。
因为程序的作用是向 CPU 发出指令,而且编程语言又是由说英语的人发明的,所以编程语言与英语祈使句类似。
Z80 CPU的寄存器结构
背景知识:计算机的硬件有三个基本要素,CPU、内存和 I/O。CPU 负责解释、执行程序,从内存或 I/O 输入数据,在内部进行运算,再把运算结果输出到内存或 I/O。内存中存放着程序,程序是指令和数据的集合。I/O 中临时存放着用于与周边设备进行输入输出的数据。
cpu要运算 ——》要有地方存储数据
这个地方就是寄存器。
与I/O寄存器不同:
储存 | 运算 | |
---|---|---|
CPU寄存器 | ✅ | ✅ |
I/O寄存器 | ✅ | ❌ |
简单理解一下:
- Z80 CPU 带有多种寄存器,包括 A、B、C、D 等字母命名的寄存器,每个寄存器都有其特定的功能。
- 此外,还有 F 寄存器,也被称为标志寄存器,用于存储运算结果的状态信息,如是否发生进位、数字大小的比较结果等。
- PC 寄存器,即程序计数器,负责存储指向下一条即将执行的指令的地址。
- SP 寄存器,即栈顶指针寄存器,用于在内存中创建出一块称为 “栈” 的临时数据存储区域。
- 还有 IX、IY 等寄存器。
标签 操作码 操作数 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
这个时候我们继续看这段代码的内容:
这段程序可以分为两部分——“设定 Z80 PIO”和“与 Z80 PIO 进行输入输出”。
把端口 A 设定为输入模式: | 以将端口 B 设定为输出模式。 | 于把由指拨开关输入的数据输出到 LED |
---|---|---|
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 |
追踪程序的运行过程
先试试这样理解
计算机执行程序的步骤。
一开始,CPU 会根据 PC 寄存器存的地址从内存读指令,然后执行指令
再更新 PC 寄存器,让它指向下一条指令的地址。
比如代码清单的程序,重置 CPU 后,PC 寄存器就会存 00000000,然后从这儿读指令。
像第一条指令 00111110 11001111,CPU 知道这是 2 个字节的指令,
就会再从 00000001 读数据 11001111,然后一起执行,
就是把 207 放到寄存器 A 里。执行完这条指令,PC 寄存器的值就会增加,
比如变成 00000010,指向下一条指令。
就这样,CPU 一直读指令、执行、更新 PC 寄存器,程序就运行起来了。
通过看 PC 寄存器的变化,就能知道程序咋运行的。
地址 | 汇编语言 | 机器语言 |
---|---|---|
00000000 | LD A, 207 | 00111110 11001111 |
00000010 | OUT (2), A | 11010011 00000010 |
00000100 | LD A, 255 | 00111110 11111111 |
00000110 | OUT (2), A | 11010011 00000010 |
00001000 | LD A, 207 | 00111110 11001111 |
00001010 | OUT (3), A | 11010011 00000011 |
00001100 | LD A, 0 | 00111110 00000000 |
00001110 | OUT (3), A | 11010011 00000011 |
00010000 | IN A, (0) | 11011011 00000000 |
00010010 | OUT (1), A | 11010011 00000001 |
再试试这样理解
首先,我们要知道程序在计算机中是怎么运行起来的呢?这主要靠中央处理器(CPU)。
一、程序启动的初始状态
当我们启动一个程序时,就好像打开了一个神秘的盒子。在这个盒子里,有一个非常重要的东西叫做程序计数器(PC)。这个 PC 就像是一个指针,它一开始会指向程序的起始地址。比如说,当我们重置 CPU 的时候,PC 寄存器会自动存储一个特定的地址,比如 00000000。这个地址就是程序开始的地方。
二、读取指令阶段
- 现在,CPU 要开始工作了。它会根据 PC 寄存器里的地址,去内存中找对应的指令。就好像你有一个地图(PC 寄存器里的地址),然后根据这个地图去一个大仓库(内存)里找特定的东西(指令)。
- 假设我们找到的第一条指令是由两个字节组成的,比如 00111110 11001111。CPU 很聪明,它能够判断出这是一个由两个字节构成的指令。所以,它会接着从下一个地址(00000001)再读出一个数据 11001111。这样,这两个数据组合在一起,CPU 就能知道这是什么指令了。
三、执行指令阶段
- CPU 读取完指令后,就会开始执行这个指令。比如说,刚才的指令是把一个数值写入到寄存器 A 中。那么,CPU 就会按照这个指令的要求,把指定的数值(从指令中解析出来的)放到寄存器 A 里。
- 执行完这条指令后,CPU 的状态会发生一些变化。可能寄存器 A 的值变了,或者一些标志位也可能被设置了。就好像你完成了一个任务后,你的状态也会有所改变一样。
四、更新程序计数器阶段
- 执行完一条指令后,CPU 要准备执行下一条指令了。这时候,PC 寄存器就要发挥作用了。它的值会增加,增加的大小取决于刚刚执行的指令的长度。如果上一条指令是两个字节,那么 PC 寄存器的值可能会增加 2。
- 这样,PC 寄存器就指向下一条指令的地址了。CPU 就可以根据这个新的地址,去内存中读取下一条指令,然后重复刚才的过程,读取指令、执行指令、更新 PC 寄存器。
通过这样的过程,程序就一步一步地在计算机中运行起来了。我们可以通过观察 PC 寄存器的变化,来追踪程序的运行流程,看看程序是怎么从第一条指令执行到最后一条指令的。
尝试手工汇编
定义:把用汇编语言编写的程序手工转换成机器语言的程序,这样的工作称为“手工汇编”。
把汇编语言程序变成机器语言,这样能让我们更明白计算机咋工作的。具体咋做呢?
就从汇编语言的第一行开始,看它是啥指令,再根据规定把里面的数换成二进制,就变成机器语言了。
然后每一行都这么弄,再把机器语言和内存地址对上号。
助记符 | 机器语言 | 时钟周期数 |
---|---|---|
LD A,num | 00111110 num | 7 |
OUT(num),A | 11010011 num | 11 |
IN A,(num) | 11011011 num | 11 |
JP num | 11000011 num | 10 |
略读:
有很多常用的机器语言指令,比如:
- ADD(加法运算)指令:用于将寄存器或内存中的数值与另一个数值相加,并将结果存回寄存器中。
- SUB(减法运算)指令:进行减法运算。
- AND(逻辑与)指令:对两个操作数进行逻辑与运算。
- OR(逻辑或)指令:进行逻辑或运算。
- XOR(逻辑异或)指令:执行逻辑异或操作。
- PUSH(入栈)指令:将数据压入栈中。
- POP(出栈)指令:从栈中弹出数据。
- CALL(调用子例程)指令:用于调用子例程。
- RET(从子例程返回)指令:在子例程执行完毕后返回主程序。
- MOV(数据传送)指令:在不同的寄存器或内存位置之间传送数据。
补充:计算机的存储
一、ROM(只读存储器)
- 特点:
- 在制造时数据就被写入,正常情况下只能读取,难以进行大量的写入操作。
- 非易失性,断电后数据不丢失。
- 存储的内容通常是固定的程序、数据或固件,比如计算机的基本输入输出系统(BIOS)。
二、磁盘(以硬盘为例)
- 特点:
- 可以进行多次读写操作。虽然相比内存速度慢很多,但比 ROM 的读取速度可能会快一些(不同类型的磁盘和 ROM 速度差异较大)。
- 非易失性,断电后数据不丢失。
- 容量通常较大,可以存储大量各种类型的数据,包括操作系统、应用程序、文档等。
三、RAM(随机存取存储器)
- 特点:
- 可以随机读写数据,速度较快。
- 易失性,断电后数据丢失。
- 通常用于暂时存储正在运行的程序和数据。
四、CPU 寄存器
- 特点:
- 位于 CPU 内部,速度极快,是计算机中速度最快的存储单元。
- 容量极小,用于暂存当前正在处理的数据和指令。
总结来说,ROM 是只读的非易失性存储器,主要存储固定内容;磁盘是可读写的非易失性存储设备,容量较大;RAM 是可读写的易失性存储器,用于临时存储数据;CPU 寄存器在 CPU 内部,速度最快但容量极小。它们在计算机系统中各自承担着不同的功能。
计算机中除了上述四块存储区域外,还有一些其他存储单元,比如:
五、缓存(Cache)
- 位置:通常位于 CPU 和主内存(RAM)之间。
- 特点:
- 速度比主内存快,但比 CPU 寄存器慢。
- 用于存储 CPU 近期可能会频繁访问的数据和指令,以减少 CPU 从主内存中读取数据的时间,提高计算机的运行速度。
六、内存
- 这不是一个实际的物理存储单元,而是一种通过硬盘空间模拟主内存的技术。
- 当主内存(RAM)不足时,操作系统会将一部分暂时不使用的内存数据存储到硬盘上的特定区域,这个区域就被称为虚拟内存。当需要这些数据时,再从虚拟内存中调回到主内存中供 CPU 使用。
七、CMOS 存储器
- 通常位于计算机主板上。
- 特点:
- 存储一些计算机的基本配置信息,如系统时间、硬件设置等。
- 由一块小型的电池供电,在断电情况下也能保存数据,但存储容量非常小。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)