机器语言3
存储型程序概念
程序存储概念
- 指令以二进制方式被编码
- 程序存储在存储器中;可以从存储器中读取程序也可以写入程序
- 存储方式与数据存储完全相同
- 简化了计算机系统的软件/硬件设计
- 存储器技术既可以存储数据,也可以存储程序
- 由于存储在存储器单元中,因此指令和数据都有地址
二进制兼容
- 程序是以二进制形式发布的
- 指令集与程序之间是强相关
- 新机器不仅能运行基于新指令编译产生的新程序,同样也最好能运行老程序
- 上述特性被称为向后兼容(backward compatible)
把指令当作数看待
如果所有的数据都是以字为单位的,这包括每个寄存器的宽度,lw
和sw
读写主存的单位是字。
由于计算机只能识别0和1,对于指令不能够直接理解,所以就可以将指令也进行编码,编码为与数据宽度相同的字,就像MIPS里所有指令的二进制编码宽度为32位
指令的32位被分为了若干域,域就是指某一区域,由于该区域带有特殊含义,所以与其它区域进行区分,分为了若干域,由此不难知道同一个域在不同指令中的含义大体是相同的。
与数据不同的是,指令的32位有域的划分,不同域代表指令的不同信息,而数据32位整体被读取为一个数据信息。
MIPS的3类指令格式
- I型指令:指令中包含立即数
lw
/sw
的偏移量是立即数;beq
/bne
同样包含有偏移srl
等移位指令:有5位立即数(移位位数),但是不属于I型指令
- J型指令:
j
和jal
jr
不是J型指令
- R型指令:所有除上的其它指令,比如
srl
和jr
R型指令格式
R型指令的构成
R型指令分为6个域:6 + 5 + 5 + 5 + 5 + 6 = 32
31 0
|-------|-------|-------|-------|-------|-------|
| 6 | 5 | 5 | 5 | 5 | 6 |
|-------|-------|-------|-------|-------|-------|
每个域都有一个名字
31 0
|-------|-------|-------|-------|-------|-------|
|opcode | rs | rt | rd | shamt | funct |
|-------|-------|-------|-------|-------|-------|
每个域都被视为一个无符号整数
- 5位域表示范围为0~31
- 6位域表示分为为0~63
其中
- opcode
代表指令操作,R型指令的opcode
固定为0b000000
- funct
与opcode
组合,定义指令的具体操作,主要服务于R型指令
- rs
指定第一个操作数(source寄存器)
- rt
指定第二个操作数(target寄存器)
- rd
指定结果回写的寄存器(destination寄存器)
注意:由于与具体指令相关,有些域是无效的
shamt
移位指令中的移位位数除了移位指令,该域固定为0
I型指令格式
指令的立即数
指的是5位或6位的数字,过短则不认为是立即数
为了使得指令格式更为统一简洁,I型指令格式尽量选择与R型指令靠拢,由于使用了立即数,所以能使用的寄存器就只有2个
I型指令的构成
31 0
|-------|-------|-------|-----------------------|
| 6 | 5 | 5 | 16 |
|-------|-------|-------|-----------------------|
域的命名
31 0
|-------|-------|-------|-----------------------|
|opcode | rs | rt | immediate |
|-------|-------|-------|-----------------------|
不难发现前3个域与R型指令相同,尤其是opcode
其中
- opcode
代表指令操作,I型指令的opcode
为非零编码,总共可以编码63条指令(勘误gxpPPT)
- rs
指定第一个操作数(source寄存器)
- rt
指定第二个操作数(target寄存器)
- immediate
无符号或有符号数字
- 无符号:位运算指令(比如and
/or
/nor
)、小于置位指令(比如slti
等)
- zero_ext()
运算前需要进行无符号拓展
- 有符号:分支指令(如beq
/bne
等)、访存指令(如lw
/sw
等)
- 以word
为单位
- sign_ext()
位运算前需要进行有符号拓展
lui
Load Upper Immediate(lui)
lui reg, imm
reg
高位写入imm
,低16
位写入0
ori
ori $rd, $rs, imm
rs
低16
位与imm
进行或运算,利用这个可以将reg
低16
位写入数字
分支指令(B类指令)
beq
和bne
31 0
|-------|-------|-------|-----------------------|
|opcode | rs | rt | immediate |
|-------|-------|-------|-----------------------|
比较rs
和rt
,根据比较结果决定是否转移
并非所有的B类指令的
rt
域都是寄存器
需要指定要转移的地址
分支指令主要用于构造if-else
,while
,for
- 转移范围通常很小(<50指令)
- 函数调用和无条件跳转用J型指令
- B类指令的基地址就是PC(即当前这条B类指令的PC值)
PC相对寻址
PC相对寻址:PC为基地址,immediate
为偏移(二进制补码)
- 基本计算方法:\(PC \leftarrow PC + 偏移\)
- 关键是得到偏移值
下一条指令的PC值的计算方法: - 比较结果为假:PC = PC + 4 - 比较结果为真:PC = (PC + 4) + (immediate * 4)
为什么
immediate
乘以4
? 因为存储器是以字节为单位的 指令都是32位长,且指令是字对齐,所以最低2位恒为0immediate
没有必要存储最低2位,所以需要乘4字对齐意味着存储地址相邻且能被4整除,即地址最低2位为0,半字对齐意味着存储地址相邻且能被2整除,即地址最低1位恒为0
由于immediate
为16位有符号数,表示范围为\(-2^{15} —— +2^{15}\)
其量纲为字,按照一行C代码对应10条指令,则可转移的C代码块大小为3000行,足够使用。
J型指令格式
I型指令的构成
31 26 25 0
|-------|---------------------------------------|
|opcode | instr_index |
|-------|---------------------------------------|
可转移范围为\(2^{28}\)即256MB
只有jr
可以转移到4GB以内的任意地址,为R型指令
汇编实战
汇编:将汇编程序转化为二进制机器码的过程
每条指令的汇编基本步骤: 1. 表示出指令类型R/I/J 2. 表示出正确的域 3. 用10进制表示各个域的值 4. 把各个域的10进制转换为2进制 5. 用16进制表示整个机器码
反汇编实战
反汇编:反汇编是汇编的逆过程,即将指令二进制代码转换为汇编代码的过程。
反汇编基本步骤:
1. 用2进制表示指令
2. 根据opcode
表示出指令类型R/I/J
3. 用10进制表示各个域的值
4. 用标识符表示各域,并添加相应的标号
5. 用汇编格式书写代码
6. 将汇编代码翻译为C