第二章 指令:计算机的语言


章节导图

CH2 指令:计算机的语言 导图.png


第一部分:MIPS-32 概述

指令的组成与 MIPS 的设计思想

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

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 指令。


逻辑按位运算:andornor 指令

  • 当两个源寄存器中,对应的位上同时为 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


逻辑移位运算:sllsrl 指令

比较 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, 0add $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, 327677FFF(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)。


决策:条件分支 beqbne

计算机和一般计算器的区别在于决策能力,即根据一定的条件选择执行何种运算的能力。最基础的判断条件是相等关系。
假设 $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 种条件判定及其伪指令

通过 sltbeqbne(严格来说还有小于立即数则置位 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:

结论:

  • 判定大于 > 或小于 < 使用 sltbeq
  • 判定大于等于 或小于等于 使用 sltbne

对于比大小的四种比较条件,可以使用伪指令:

  • 小于则分支: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。

使用移位量的两条逻辑移位指令 sllsrl 也属于 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 字段表示与存储器单元交换数据的寄存器。

lwsw 指令的操作码分别为 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 语言中的函数(一种典型的过程)是结构化编程的强大工具。函数获取参数、执行运算、返回结果,就好比侦探拿着一份计划书去执行任务,再带来想要的结果。过程的执行过程如下:

  1. 主程序(调用者)将参数放在过程(被调用者)可以取用的特定位置(参数寄存器)。
  2. 主程序将控制权交给过程。
  3. 过程申请并获得存储资源。
  4. 过程执行。
  5. 过程将结果的值放在主程序可以取用的特定位置(值寄存器)。
  6. 过程把控制权返还给主程序,执行调用过程指令的下一条指令(通过 返回地址寄存器 找到位置)。

支持过程的三大寄存器

  1. 参数寄存器(argument reg)$a0~$a3,用于存放主程序传递给过程的参数。
  2. 值寄存器(value reg)$v0~$v1,用于存放过程返回给主程序的结果。
  3. 返回地址寄存器(return address reg)$ra,用于存放主程序调用过程后继续执行的地址。

jal-jr 指令对:程序计数器

  • **跳转并链接(jump and link)**指令 jal

    • 无条件跳转到一个标签。
    • 将下一条指令的地址放入返回地址寄存器 $ra
    • 由调用者主程序使用,用于调用过程。
    • 示例:jal Label
  • **寄存器跳转(jump reg)**指令 jr

    • 跳转到某一寄存器存储的 32 位地址。
    • 基本上只和返回地址寄存器 $ra 搭配。
    • 由被调用者过程使用,用于返回主程序。
    • 示例:jr $ra

综合练习 5:数组清零函数(叶过程)

1
2
3
void clear(int a[], int size) {
for (i = 0; i < size; i++) a[i] = 0;
}

保存寄存器的压栈和出栈:栈指针 $sp

在过程调用前,主程序往往已经将自己要用的变量放在了保存寄存器中。如果过程要使用保存寄存器,要把主程序已经使用的保存寄存器入栈。
栈在内存中以高地址为栈底,低地址为栈顶,即栈从高地址向低地址“生长”。栈指针(stack pointer)永远指向栈顶。
入栈时,先把 $sp 减去待保存的保存寄存器个数的 4 倍(因为每个寄存器占用 4 字节),再用 sw 将保存寄存器存入栈中(方向从栈底到栈顶)。过程结束时,把这些数据出栈、放回保存寄存器,供主程序继续使用。


综合练习 6:运算函数(叶过程)

1
2
3
4
5
int cal(int g, int h, int i, int j) {
int f;
f = (g + h) - (i + j);
return f;
}

假设 f 存储在 $s0 中。


综合练习 6 改进:减少指令条数

通过优化指令,可以减少过程中的指令数量,提高执行效率。


嵌套过程调用:综合练习 7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int sum_of_squares(int a[], int size) {
int i = 0;
int sum = 0;
for (i = 0; i < size; i++) {
sum = sum + square(a[i]);
}
return sum;
}

int square(int a) {
int square;
square = a * a;
return square;
}

需要压栈保存的寄存器

  • 保存寄存器($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

程序在内存中包含五段,地址从低到高分别为:

  1. 保留段
  2. 正文段(代码段),保存指令
  3. 静态数据段,保存静态数据
  4. 动态数据段(堆),从低往高“生长”
  5. 栈,从高往低“生长”

栈和堆此消彼长,实现了内存空间的高效利用。


第四部分:五种寻址方式


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 型指令包括:

  1. 运算指令:addsubandornor(三寄存器操作数)
  2. 运算指令:sllsrl(双寄存器操作数,rs 不使用,置为 0)
  3. 决策指令:slt(三寄存器操作数)
  4. 决策指令:jr(单寄存器操作数)

I 型:立即数寻址和基址偏移寻址

  • 立即数寻址(immediate addressing):第三个操作数(第二个源操作数)是常数的指令采用立即数寻址。具体包括 addiori 两条指令。
  • 基址偏移寻址(base addressing + displacement addressing):两条数据传送指令 lwsw,将基址寄存器和偏移量相加的内存寻址方式。

例如:lw $t0, 12($s0) 表示将 $s0 中的基地址与偏移量 12 相加,得到目标地址。


I 型:PC 相对寻址

两条条件分支指令 beqbne 使用 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),包含 jjal 两条指令。

执行 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 指令。

寻址方式小结

  1. R 型的寄存器寻址:操作数为 1 个/2 个/3 个寄存器的数据。
  2. I 型的立即数寻址addiluiori 三条立即数指令,其中一个操作数是指令字段中的常数。
  3. I 型的基址偏移寻址lwsw 两条访存指令,将 rs 中的基地址和偏移量直接相加,得到偏移地址。
  4. I 型的 PC 相对寻址beqbne 两条条件分支指令,分支指令中的 PC 相对地址(可正可负的字偏移量)左移两位(×4)形成字节偏移量,再和 PC + 4 中的字节地址相加,形成分支目标地址。
  5. 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 个翻译层次

  1. 编译器:将高级语言文件(.c)翻译成汇编语言文件(.asm)。
  2. 汇编器:首先把伪指令替换为等价的真正指令,再将汇编语言翻译成机器语言目标文件(.obj)。
  3. 链接器:把目标文件和静态链接库(.lib)、动态链接库(.dll)拼接成可执行文件(.exe)。
  4. 加载器:将可执行文件放入内存,装载执行。

字符支持与同步指令对:LL-SC

  • 为了处理 C 的 8 位 ASCII 字符,MIPS 提供字节传送指令 lbsb
  • 为了支持 Java 的 16 位 Unicode 字符,MIPS 提供半字传送指令 lhsh
  • 当多个程序访问同一个内存单元且存在写操作时,MIPS 提供链接取数 ll 指令和条件存数 sc 指令,以指定程序操作数据的顺序。

ARM 和 x86 简介

  • ARM

    • 同 MIPS 一样是 RISC 架构,具有优秀的能耗表现,广泛应用于移动端和嵌入式平台。
    • 主要区别:通用寄存器更少(16 个),寻址方式更多(9 种)。
  • x86

    • 由 Intel 和 AMD 主导的 CISC 架构,指令集庞大(2018 年约 1400 条)。
    • 指令长度从 1B 到 15B 不等,本质复杂但因 IBM PC 的成功而占据巨大市场份额。
    • Intel 将 x86 移动化的尝试屡屡碰壁,而苹果将 ARM 电脑化的实践却高歌猛进。

复习题

  1. 为了实现过程调用,我们引入了哪三类寄存器和哪个指令对?
  2. 三类寄存器分别存放什么?指令对中的两条指令分别由谁使用,完成什么功能?
  3. 当过程要使用保存寄存器时,要进行什么操作?
  4. 为什么过程内部的变量优先使用临时寄存器?
  5. 复习综合练习 5~7,熟悉 for 循环、清零、函数调用等常见的 C

文章作者: MIKA
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MIKA !
  目录