章节导图

第一部分:MIPS-32 概述
指令的组成与 MIPS 的设计思想
计算机执行任何程序,本质上都是在执行机器语言指令(instruction)。每条指令都是一条 0-1 串。
指令首先要指明执行什么操作,通常用 0-1 串中的前几位来表示,称为操作码(opcode)。
指令还要指出需要操作的数据来自哪里、操作后的结果数据放回哪里,通常用 0-1 串中的剩余位来表示,称为操作数(operand)或地址码(address code)。
大部分操作数都是一个地址编号,告诉 CPU 从哪里取得数据、向哪里放回数据,因此操作数通常也叫做地址码。

MIPS 作为一种 RISC 指令集,设计力求保证硬件设备的简单性。在我们讲解的 32 位 MIPS 汇编语言(MIPS-32)中,所有指令都是 32 位长。
MIPS-32 中的通用寄存器
=MIPS 中运算操作的操作数必须来自寄存器(register)或者指令本身。=
寄存器是一种位于 CPU 内部、比 cache 更小更快的存储器,用来暂时存放运算的源数据和结果。
一些寄存器是专用的,如存放执行中指令的地址的程序计数器(PC)。
与此相对应,用于暂时存放运算数据的寄存器称为通用寄存器。
MIPS 中一共有 32 个 32 位的寄存器,共 128B(大部分架构都采用 16 或 32 个寄存器)。我们约定:
- 程序中的变量存放在**保存寄存器(saved registers)中:
$s0~$s7,共 8 个。 - 运算的临时变量、中间变量存放在**临时寄存器(temporary registers)中:
$t0~$t7,共 8 个。 - 还有一个零寄存器,永远存放 32 位的 0,写作
$zero。(注意,已经被硬编码为0,无法被修改)
第二部分:三类汇编指令
算术运算:加法和减法
C 赋值语句:c = a + b;
加法指令:add c, a, b,将 a 和 b 中的数据相加,并将结果存放在 c 中。
再次强调:
MIPS 中运算的操作数必须来自 #card
寄存器或者指令本身!
假设变量 a、b、c 分别存放在寄存器 $s0、$s1、$s2 中,这条指令就应当写为:
add $s2, $s0, $s1
加法中两个加数可以对换,但减法不行,因此 c = a - b; 必须写作:
sub $s2, $s0, $s1
运算的“原材料”a 和 b 对应的寄存器 $s0、$s1 分别称为源操作数 1(src1)和源操作数 2(src2),运算的结果 c 对应的寄存器 $s2 称为目的操作数(des)**。
加减指令的通式:add/sub des, src1, src2。
算术运算:加立即数
在 i++ 即 i = i + 1; 这条赋值语句中,有个确定的常数 1。
与其采取额外的步骤将 1 装入某个寄存器,不如让指令本身包含这个 1。
假设变量 i 位于寄存器 $s0,我们把加法指令的第二个源操作数改为常数 1:
addi $s0, $s0, 1
这就成了加立即数(add immediate)指令。因为 addi 指令中的立即数可以取负数(对立即数取负后相加),因此 MIPS 中没有 subi 指令。
逻辑按位运算:and、or、nor 指令
- 当两个源寄存器中,对应的位上同时为 1 时,
and操作结果为 1。 - 当两个源寄存器中,对应的位上至少有一个为 1 时,
or操作结果为 1。
假设:
$t0 = 0000 0000 0000 0000 0000 0000 0000 1001
$t1 = 0000 0000 0000 0000 0000 0000 0000 1100
执行下列两条指令后,$t2 中的数据分别变为多少?
and $t2, $t0, $t1
or $t2, $t0, $t1
任何数据与 0 进行或非 nor 操作,都会 0/1 反转。
执行下列指令后,$t2 中的数据会变为多少?
nor $t2, $t0, $zero
逻辑移位运算:sll 和 srl 指令
比较 12 和 120 两个十进制数,通过在最低位的右边添加一个 0,变为了 10 倍。
比较 11 和 110 两个二进制数,通过在最低位的右边添加一个 0,变为了多少倍?1100 呢?
逻辑左移(shift left logic)指令让寄存器中的数据整体往左移动指定的位数,并在右边空出来的位上补 0。
假设 $s2 = 29个0 + 101,逻辑左移两位后,放到寄存器 $s0 中:
sll $s0, $s2, 2
这里的 2 不是 addi 指令中的立即数,而是告诉计算机移动几位的移位量(shift amount)。通过这样一句指令,我们实际上完成了 x4 的运算!
x2、x8、x128 时,移位量分别是多少?
srl 指令当然可以实现 /2 运算,但使用场景不多,不额外讨论。
综合练习 1:变量运算与赋值
翻译以下 C 语句:
result = a - 10 + (b + c * 5);
寄存器—存储器数据传送:lw 指令
运算指令的操作数必须来自于寄存器/指令本身,但通用寄存器一共只有 128B。
数组元素却可以占据成千上万个字节,只能存放在内存中。
这时,我们把数组第一个元素(a[0])的 32 位地址,称为数组的基址,放在寄存器中。
基址加上要找的元素的下标,就组成了这个元素的地址。
如果源操作数在内存中,是数组 a 的 5 号元素(第六个元素),数组 a 的基址存放在 $s1 中,那么 a[5] 的地址就表示为 5($s1)。
计算机会自动计算 $s1 中的基址和偏移量 5 的和,找到 a[5] 的地址。
将 a[5] 从内存传送到寄存器 $s0,使用取字指令(load word):
lw $s0, 5($s1)
寄存器—存储器数据传送:字与 sw 指令
MIPS 的通用寄存器都是 32 位长,这个长度就是 MIPS 体系结构的字长,通常代表了参与运算的数据的长度。因此我们约定:整门课程中,1 字 = 32b = 4B。
a[5] 相对于 a[0],在内存中的距离是 5 个字,而不是 5 个字节。又因为内存按字节编址,即内存每个字节都有一个特定的编号,所以偏移量应该是 5x4 = 20 个字节。
a[5] 的地址应表示成 20($s1)。于是取数指令变为:
lw $s0, 20($s1)
如果我们要把 $t0 中的运算结果送回内存中的 a[2],需要用到存字指令(store word):
sw $t0, 8($s1)
寄存器间数据传送:装载立即数到寄存器
如果我们需要把数从 $t0 保存到存放某变量的 $s1 中,怎么实现?
MIPS 没有专门的寄存器间移动数据的指令,但可以通过把源寄存器中的数据加上 0 再保存到目标寄存器中,实现相同的功能:
addi $s1, $t0, 0 或 add $s1, $t0, $zero
这个功能可以用 move 伪指令来代替:
move $s1, $t0
假如我们要把一个常数 10 装入寄存器 $s2,同样可以采用 addi 指令:
addi $s2, $zero, 10
或使用取立即数(load immediate)伪指令:
li $s2, 10
装载 32 位立即数到寄存器
我们说可以用 addi 指令向寄存器装载立即数:addi $s2, $zero, 10,但 addi 指令中的立即数 10 只能占用 32 位指令中的一部分(16 位,稍后介绍指令格式)。
16 位只能表示 2^16 即六万多个数,寄存器却能容纳 2^32 即 40 多亿个数。
假设我们要向寄存器 $s2 装载一个 32 位的立即数:10A2 7FFF(16 进制)。 翻译为指令 #card
我们必须先用取高位立即数(load upper immediate)指令,把 10A2 放入 $s2 的高 16 位:
lui $s2, 4258(十六进制的 10A2 等于十进制的 4258)
再让 $s2 与低 16 位的立即数 7FFF 进行或运算:
ori $s2, $s2, 32767(7FFF(16)=32767(10))
这样,就分两步把 32 位立即数装载到了 32 位的寄存器中。
不能使用 addi 替代 ori 指令,如果低 16 位的最高位是 1,addi 会把它理解为负数。
综合练习 2:数组元素运算与赋值
a[i] = a[0] + 100000;
假设数组 a 的基址位于 $s0,变量 i 位于 $s1。
100000(10)=186A0(16),1(16)=1(10),86A0(16)=34464(10)。
决策:条件分支 beq 和 bne
计算机和一般计算器的区别在于决策能力,即根据一定的条件选择执行何种运算的能力。最基础的判断条件是相等关系。
假设 $s0 = 0,$s1 = 0,$s2 = 1:
- **相等则分支(branch if equal)**指令在两个源操作数寄存器中的值相同时分支,分支以分支标签表示:
beq $s0, $s1, Label - **不等则分支(branch if not equal)**指令在值不同时分支到标签:
bne $s0, $s2, Label
如果不发生分支,则继续执行内存中相邻的下一条指令。
综合练习 3:if-else 语句(无条件跳转 j 和条件分支)
if (i == j) f = g + h; else f = g - h;
假设 f、g、h、i、j 分别存放在 $s0~$s4 中。
结论:判定相等 == 使用 bne,判断不等 != 使用 beq。 #card
决策:小于则置位 slt
除了相等、不等关系,我们还经常比较两个数的大小。MIPS 有一条**小于则置位(set on less than)**指令 slt。
置位:将一位设置为 1;复位:将一位设置为 0。
假设 $s0 = 0,$s1 = 0,$s2 = 1:
slt $t0, $s0, $s2
源操作数 1 < 源操作数 2 吗?Yes!此时把目的操作数寄存器 $t0 置位为 1。
slt $t0, $s0, $s1
源操作数 1 < 源操作数 2 吗?No!此时把目的操作数寄存器 $t0 复位为 0。
6 种条件判定及其伪指令
通过 slt、beq、bne(严格来说还有小于立即数则置位 slti 指令,不作讨论)指令的各种组合,我们就能够实现全部六种比较条件,即六种值为真或假的布尔表达式。
例如:
if (i < j) f = g + h;
slt $t0, i, j(当 i < j 时,把 $t0 置为 1,否则为 0)
beq $t0, $zero, Else(当 $t0 为 0 时,执行 else 后的语句)
add f, g, h(否则顺着执行 if 后的语句)
j Exit(加法完成后退出 if-else 语句)
Else:
sub f, g, h(else)
Exit:
结论:
- 判定大于
>或小于<使用slt和beq。 - 判定大于等于
≥或小于等于≤使用slt和bne。
对于比大小的四种比较条件,可以使用伪指令:
- 小于则分支:
blt - 大于则分支:
bgt - 小于等于则分支:
ble - 大于等于则分支:
bge
综合练习 4:while 循环
while (a[i] == k) i++;
假设 i, k 分别存放在 $s3 和 $s5 中,a 的基址存放在 $s6 中。
MIPS 汇编指令小结
| 类别 | 指令名称 | 指令格式 |
|---|---|---|
| 一、运算指令 | 加减法指令 | add/sub des, src1, src2 |
| 加立即数指令 | addi des, src1, i |
|
| 与/或/或非指令 | and/or/nor des, src1, src2 |
|
| 或立即数指令 | ori des, src1, i |
|
| 逻辑左移/右移指令 | sll/srl des, src1, shamt |
|
| 二、数据传送指令 | 取字/存字指令 | lw/sw reg, num(reg) |
| 取高位立即数指令 | lui reg, i |
|
| 三、决策指令 | 相等/不等则分支指令 | beq/bne src1, src2, Label |
| 小于则置位指令 | slt des, src1, src2 |
|
| 跳转指令 | j Label |
|
| 伪指令 | 条件分支伪指令 | blt/bgt/ble/bge src1, src2, Label |
| 寄存器数据传送伪指令 | move des, src |
指令格式:R 型
指令中含三个寄存器的运算指令都属于 R 型(register type)指令。
32 位的 MIPS 指令一共分为 6 个字段:
| 字段 | 含义 | 位数 |
|---|---|---|
| op | 操作码(opcode) | 6 位 |
| rs | 源操作数寄存器(register source) | 5 位 |
| rt | 第二个源操作数寄存器(register target) | 5 位 |
| rd | 目的寄存器(register destination) | 5 位 |
| shamt | 移位量(shift amount) | 5 位 |
| funct | 功能码(function code) | 6 位 |
R 型指令的操作码 op 都是 6 个 0,由 6 位功能码 funct 进一步指定执行什么操作。
例如,add 指令的功能码为 32,sub 指令的功能码为 34。
使用移位量的两条逻辑移位指令 sll 和 srl 也属于 R 型指令。因为没有第二个源操作数寄存器,rt 被置为 0。
指令格式:I 型(立即数)
有两条“目的 reg + 源 reg + 立即数”格式的指令:
addi des, src1, i
ori des, src1, i
通过把 R 型指令中的后三个字段拼接成一个 16 位的立即数字段,让指令本身包含常数,这样的指令属于 I 型(immediate type)指令。
例如,addi 指令的操作码为 8,rd 字段被合并,rt 成为目的寄存器。
指令格式:I 型(偏移量)
lw/sw reg, num(reg)
两条数据传送指令也包含两个寄存器和一个常数,同样属于 I 型指令。此时,16 位立即数字段的含义发生了改变,表示数组元素相对于数组基址的地址偏移量。
无论是 lw 还是 sw 指令,都是由 rs 字段表示的寄存器值与 address 字段相加,得到存储器单元地址。rt 字段表示与存储器单元交换数据的寄存器。
lw 和 sw 指令的操作码分别为 35 和 43。
指令格式:I 型(标签)
beq/bne src1, src2, Label
在这两条条件分支指令中,同样使用了两个寄存器,还有一个分支标签的地址,用 16 位立即数字段表示(也就变成了 Address 字段),也属于 I 型指令。
例如,当 i($s0)和 j($s1)相等时分支到地址为 10000 的标签 Else:
beq $s0, $s1, Else
翻译为机器语言为:
000100 00010 00011 000000001000(具体地址需根据寻址方式计算)
这里的 10000 实际上并不是 Else 标签指向指令的地址,具体说明见寻址方式部分。
机器语言指令格式小结
| 指令 | 格式 | 示例 | 说明 |
|---|---|---|---|
| add | R | 000000 10001 10010 01000 00000 100000 |
加法指令 |
| sub | R | 000000 10001 10010 01000 00000 100010 |
减法指令 |
| addi | I | 001000 10001 01000 000000000100 |
加立即数指令 |
| lw | I | 100011 10001 01000 000000000100 |
取字指令 |
| sw | I | 101011 10001 01000 000000000100 |
存字指令 |
- R 型指令:操作码为 6 个 0,功能码指定具体操作。
- I 型指令:操作码 + 寄存器 + 立即数/偏移量/地址。
- J 型指令:操作码 + 目标地址(伪直接寻址)。
第三部分:过程支持
过程(函数)的执行过程
C 语言中的函数(一种典型的过程)是结构化编程的强大工具。函数获取参数、执行运算、返回结果,就好比侦探拿着一份计划书去执行任务,再带来想要的结果。过程的执行过程如下:
- 主程序(调用者)将参数放在过程(被调用者)可以取用的特定位置(参数寄存器)。
- 主程序将控制权交给过程。
- 过程申请并获得存储资源。
- 过程执行。
- 过程将结果的值放在主程序可以取用的特定位置(值寄存器)。
- 过程把控制权返还给主程序,执行调用过程指令的下一条指令(通过 返回地址寄存器 找到位置)。
支持过程的三大寄存器
- 参数寄存器(argument reg):
$a0~$a3,用于存放主程序传递给过程的参数。 - 值寄存器(value reg):
$v0~$v1,用于存放过程返回给主程序的结果。 - 返回地址寄存器(return address reg):
$ra,用于存放主程序调用过程后继续执行的地址。
jal-jr 指令对:程序计数器
-
**跳转并链接(jump and link)**指令
jal:- 无条件跳转到一个标签。
- 将下一条指令的地址放入返回地址寄存器
$ra。 - 由调用者主程序使用,用于调用过程。
- 示例:
jal Label
-
**寄存器跳转(jump reg)**指令
jr:- 跳转到某一寄存器存储的 32 位地址。
- 基本上只和返回地址寄存器
$ra搭配。 - 由被调用者过程使用,用于返回主程序。
- 示例:
jr $ra
综合练习 5:数组清零函数(叶过程)
1 | void clear(int a[], int size) { |
保存寄存器的压栈和出栈:栈指针 $sp
在过程调用前,主程序往往已经将自己要用的变量放在了保存寄存器中。如果过程要使用保存寄存器,要把主程序已经使用的保存寄存器入栈。
栈在内存中以高地址为栈底,低地址为栈顶,即栈从高地址向低地址“生长”。栈指针(stack pointer)永远指向栈顶。
入栈时,先把 $sp 减去待保存的保存寄存器个数的 4 倍(因为每个寄存器占用 4 字节),再用 sw 将保存寄存器存入栈中(方向从栈底到栈顶)。过程结束时,把这些数据出栈、放回保存寄存器,供主程序继续使用。
综合练习 6:运算函数(叶过程)
1 | int cal(int g, int h, int i, int j) { |
假设 f 存储在 $s0 中。
综合练习 6 改进:减少指令条数
通过优化指令,可以减少过程中的指令数量,提高执行效率。
嵌套过程调用:综合练习 7
1 | int sum_of_squares(int a[], int size) { |
需要压栈保存的寄存器
- 保存寄存器(
$s0~$s7):存放主程序的变量,需要由过程开始时压栈保存,结束时出栈恢复。 - 返回地址寄存器(
$ra):在嵌套调用中,外层函数通过jal修改$ra,因此需要压栈保存。 - 栈指针寄存器(
$sp):需要保存当前栈的状态。
进阶内容:复杂 MIPS 程序示例
- 2.8 节(68 页)提供了一个递归嵌套调用过程计算阶乘的 MIPS 程序。
- 2.13 节(90 页)提供了一个冒泡排序过程嵌套交换过程的 MIPS 程序。
- 习题 2.27(114 页)考察双层 for 循环的翻译。
- 习题 2.34(115 页)考察自嵌套调用的多参数过程的翻译。
过程帧与帧指针 $fp
为了标记运行中过程建立的栈,除了栈顶的栈指针 $sp,还可以加一个帧指针(frame pointer)$fp 指向栈底(即过程帧的第一个字)。$fp 和 $sp 之间的空间由正在运行的过程使用,称为过程帧,也叫活动记录。
全局指针 $gp:程序的内存分配
为了便于寻找位置固定的数据(主程序使用的变量,以及声明为 static 的变量,统称静态变量),使用一个固定指向静态数据区某一位置的全局指针(global pointer)$gp。
程序在内存中包含五段,地址从低到高分别为:
- 保留段
- 正文段(代码段),保存指令
- 静态数据段,保存静态数据
- 动态数据段(堆),从低往高“生长”
- 栈,从高往低“生长”
栈和堆此消彼长,实现了内存空间的高效利用。
第四部分:五种寻址方式
32 个通用寄存器及其编号
| 寄存器 | 名称 | 编号 |
|---|---|---|
$zero |
零寄存器 | 0 |
$v0~$v1 |
返回值寄存器 | 2~3 |
$a0~$a3 |
参数寄存器 | 4~7 |
$t0~$t7 |
临时寄存器 | 8~15 |
$s0~$s7 |
保存寄存器 | 16~23 |
$t8~$t9 |
额外的临时寄存器 | 24~25 |
$gp |
全局指针 | 28 |
$sp |
栈指针 | 29 |
$fp |
帧指针 | 30 |
$ra |
返回地址寄存器 | 31 |
R 型:寄存器寻址
所有操作数都是寄存器的指令采用寄存器寻址(register addressing)。操作数个数从一个到三个不等。
R 型指令包括:
- 运算指令:
add、sub、and、or、nor(三寄存器操作数) - 运算指令:
sll、srl(双寄存器操作数,rs不使用,置为 0) - 决策指令:
slt(三寄存器操作数) - 决策指令:
jr(单寄存器操作数)
I 型:立即数寻址和基址偏移寻址
- 立即数寻址(immediate addressing):第三个操作数(第二个源操作数)是常数的指令采用立即数寻址。具体包括
addi、ori两条指令。 - 基址偏移寻址(base addressing + displacement addressing):两条数据传送指令
lw和sw,将基址寄存器和偏移量相加的内存寻址方式。
例如:lw $t0, 12($s0) 表示将 $s0 中的基地址与偏移量 12 相加,得到目标地址。
I 型:PC 相对寻址
两条条件分支指令 beq 和 bne 使用 PC 相对寻址(PC-relative addressing)。
在汇编语言中,使用标签表示分支目标地址,标签翻译成机器语言后是一个整数,表示从当前指令地址出发到达分支目标地址的距离。
程序计数器(PC)中保存了执行中指令的地址。分支指令中的 16 位分支地址是一个二进制补码,可正可负,表示以 PC + 4 为基准相加的字地址数目。
计算公式:
分支 32 位地址 = PC + 4 + 字地址偏移量 × 4
例如,地址为 1000(十进制)的 beq reg1, reg2, 4,其分支地址为:
1000 + 4 + 4 × 4 = 1020。
J 型:伪直接寻址
J 型指令只需要操作码和目标地址两个字段,形式上最为简单。J 型指令采用伪直接寻址(pseudodirect addressing),包含 j 和 jal 两条指令。
执行 J 型指令时,先将 26 位字地址左移两位(右侧补 0)形成 28 位字节地址,再和 PC 的高四位拼接成 32 位地址。
计算公式:
32 位跳转目标地址 = PC 高四位 + 26 位字地址 × 4
例如,PC 高四位为 1010 的 j 0000 0000 0000 0000 0000 0000 0000 01,其跳转地址为:
1010 0000 0000 0000 0000 0000 0000 0100。
扩大分支与跳转的范围
- PC 相对寻址:以 PC + 4 为基准,加上一个可正可负的 16 位补码字地址,寻址范围为:
(PC + 4) - 2^17 ~ (PC + 4) + 2^17 - 4,大约是分支前后各 128KB。 - 伪直接寻址:用 PC 中当前指令地址的高四位拼接指令中的 26 位字地址,寻址范围为:
和 PC 高四位相同的一切地址,一个 256MB 的地址块。
为了扩大寻址范围:
- 分支到更远距离:可以将
beq/bne取反,下接一条可能绕过的j指令。 - 跳转到更远距离:可以先将 32 位地址装载到某临时寄存器,再用
jr指令。
寻址方式小结
- R 型的寄存器寻址:操作数为 1 个/2 个/3 个寄存器的数据。
- I 型的立即数寻址:
addi、lui、ori三条立即数指令,其中一个操作数是指令字段中的常数。 - I 型的基址偏移寻址:
lw、sw两条访存指令,将rs中的基地址和偏移量直接相加,得到偏移地址。 - I 型的 PC 相对寻址:
beq、bne两条条件分支指令,分支指令中的 PC 相对地址(可正可负的字偏移量)左移两位(×4)形成字节偏移量,再和 PC + 4 中的字节地址相加,形成分支目标地址。 - J 型的伪直接寻址:将 26 位字地址左移两位(×4)形成 28 位字节地址,再和 PC(实际上也是 PC + 4)的高四位拼接成 32 位跳转目标地址。
MIPS 汇编指令小结
| 类别 | 指令名称 | 汇编指令 | 指令格式与寻址方式 |
|---|---|---|---|
| 一、运算指令 | 加减法指令 | add/sub des, src1, src2 |
R |
| 加立即数指令 | addi des, src1, i |
I:立即数寻址 | |
| 与/或/或非指令 | and/or/nor des, src1, src2 |
R | |
| 或立即数指令 | ori des, src1, i |
I:立即数寻址 | |
| 逻辑左移/右移指令 | sll/srl des, src1, shamt |
R | |
| 二、数据传送指令 | 取字/存字指令 | lw/sw reg, num(reg) |
I:基址偏移寻址 |
| 取高位立即数指令 | lui reg, i |
/ | |
| 三、决策指令 | 相等/不等则分支指令 | beq/bne src1, src2, Label |
I:PC 相对寻址 |
| 小于则置位指令 | slt des, src1, src2 |
R | |
| 无条件跳转 | 跳转指令 j Label |
J | |
| 四、过程支持指令 | 跳转并链接 | jal Label |
J |
| 寄存器跳转 | jr $ra |
R |
第五部分:C 程序执行过程
C 语言的 4 个翻译层次
- 编译器:将高级语言文件(.c)翻译成汇编语言文件(.asm)。
- 汇编器:首先把伪指令替换为等价的真正指令,再将汇编语言翻译成机器语言目标文件(.obj)。
- 链接器:把目标文件和静态链接库(.lib)、动态链接库(.dll)拼接成可执行文件(.exe)。
- 加载器:将可执行文件放入内存,装载执行。
字符支持与同步指令对:LL-SC
- 为了处理 C 的 8 位 ASCII 字符,MIPS 提供字节传送指令
lb和sb。 - 为了支持 Java 的 16 位 Unicode 字符,MIPS 提供半字传送指令
lh和sh。 - 当多个程序访问同一个内存单元且存在写操作时,MIPS 提供链接取数
ll指令和条件存数sc指令,以指定程序操作数据的顺序。
ARM 和 x86 简介
-
ARM:
- 同 MIPS 一样是 RISC 架构,具有优秀的能耗表现,广泛应用于移动端和嵌入式平台。
- 主要区别:通用寄存器更少(16 个),寻址方式更多(9 种)。
-
x86:
- 由 Intel 和 AMD 主导的 CISC 架构,指令集庞大(2018 年约 1400 条)。
- 指令长度从 1B 到 15B 不等,本质复杂但因 IBM PC 的成功而占据巨大市场份额。
- Intel 将 x86 移动化的尝试屡屡碰壁,而苹果将 ARM 电脑化的实践却高歌猛进。
复习题
- 为了实现过程调用,我们引入了哪三类寄存器和哪个指令对?
- 三类寄存器分别存放什么?指令对中的两条指令分别由谁使用,完成什么功能?
- 当过程要使用保存寄存器时,要进行什么操作?
- 为什么过程内部的变量优先使用临时寄存器?
- 复习综合练习 5~7,熟悉 for 循环、清零、函数调用等常见的 C