计算机组成与设计:指令-计算机的语言

2.1 引言

计算机语言中的基本单词成为指令,一台计算机的全部指令成为该计算机的指令集。

不同的指令集具有相似性,一方面是因为所有计算机都是基于基本原理相似的硬件技术所构建;另一方面所有计算机都必须提供一些基本操作;此外计算机的设计者有一个共同的目标:找到一种语言,可方便硬件和编译器的设计,且使性能最佳,同时成本和功耗最低。

 MIPS操作数之32个寄存器

寄存器名字 寄存器编号 寄存器功能
$zero $0 恒等于零
$at $1 被汇编器保留,用于处理大的常数
$v0 – $v1 $2-$3 存放函数返回值
$a0 – $a3 $4-$7 传递函数参数
$t0 – $t7 $8-$15 存放临时变量
$s0 – $s7 $16-$23 存放需要保存的临时值
$t8 – $t9 $24-$25 额外的存放临时变量
$k0 – $k1 $26-$27 用于操作系统内核
$gp $28 指向全局变量的指针
$sp $29 指向栈顶的指针
$fp $30 指向栈帧[^footnote2]的指针
$ra $31 返回地址,用于函数调用

 MIPS汇编语言指令

MIPS汇编语言
类别 指令 举例 含义 备注
算术运算 add $s1, $s2, $s3 $s1 = $s2 + $s3 三个操作数;检测溢出
sub $s1, $s2, $s3 $s1 = $s2 - $s3 三个操作数;检测溢出
加立即数 addi $s1, $s2, 100 $s1 = $s2 + 100 加常数,检测溢出
无符号加 addu $s1, $s2, $s3 $s1 = $s2 + $s3 三个操作数;不检测溢出
无符号减 subu $s1, $s2, $s3 $s1 = $s2 - $s3 三个操作数;不检测溢出
无符号加立即数 addiu $s1, $s2, 100 $s1 = $s2 + 100 加常数;不检测溢出
从协处理器寄存器中获得 mfc0 $s1, $epc $s1 = $epc 复制异常PC到专用寄存器
mult $s2, $s3 Hi, Lo = $s2 × $s3 64位有符号积存在Hi,Lo中
无符号乘 multu $s2, $s3 Hi, Lo = $s2 × $s3 64位无符号积存在Hi,Lo中
div $s2, $s3 Lo = $s2 / $s3
Hi = $s2 mod $s3
Lo = 商, Hi = 余
无符号除 divu $s2, $s3 Lo = $s2 / $s3
Hi = $s2 mod $s3
无符号商和余数
从Hi中获得 mfhi $s1 $s1 = Hi 用来获得Hi的拷贝
从Lo中获得 mflo $s1 $s1 = Lo 用来获得Lo的拷贝
数据传输 取字 lw $s1, 20($s2) $s1 = Memory[$s2 + 20] 将一个字从内存取到寄存器中
存字 sw $s1, 20($s2) Memory[$s2 + 20] = $s1 将一个字从寄存器中存到内存中
取无符号半字 lhu $s1, 20($s2) $s1 = Memory[$s2 + 20] 将半个字从内存取到寄存器中
存半字 sh $s1, 20($s2) Memory[$s2 + 20] = $s1 将半个字从寄存器中存到内存中
取无符号字节 lbu $s1, 20($s2) $s1 = Memory[$s2 + 20] 将一个字节从内存取到寄存器中
存字节 sb $s1, 20($s2) Memory[$s2 + 20] = $s1 将一个字节从寄存器中存到内存中
取链接字 ll $s1, 20($s2) $s1 = Memory[$s2 + 20] 取字作为原子交换的前半部分
存条件字 sc $s2, 20($s2) Memory[$s2 + 20] = $s1;
$s1 = 0或1
存字作为原子交换的后半部分
立即数读入高16位 lui $s1, 100 $s1 = 100 * 2^16 取立即数并放在高16位
逻辑运算 AND $s1, $s2, $s3 $s1 = $s2 & $s3 三个寄存器操作数;按位与
OR $s1, $s2, $s3 $s1 = $s2 | $s3 三个寄存器操作数;按位或
或非 NOR $s1, $s2, $s3 $s1 ~($s2 | $s3) 三个寄存器操作数;按位或非
与立即数 ANDi $s1, $s2, 100 $s1 = $s2 & 100 和常数按位与
或立即数 Ori $s1, $s2, 100 $s1 = $s2 | 100 和常数按位或
逻辑左移 sll $s1, $s2, 10 $s1 = $s2 << 10 根据常数左移相应位
逻辑右移 srl $s1, $s2, 10 $s1 = $s2 >> 10 根据常数右移相应位
条件跳转 相等时跳转 beq $s1, $s2, 25 if($s1 == $s2)跳至PC + 4 + 100 相等检测; 和PC相关的跳转
不相等时跳转 bne $s1, $s2, 25 if($s1 != $s2)跳至PC + 4 + 100 不相等检测; 和PC相关的跳转
小于时置位 slt $s1, $s2, $s3 if($s2 < $s3) $s1 = 1;否则 $s1 = 0 比较是否小于;补码形式
小于立即数时置位 slti $s1, $s2, 100 if($s2 < 100) $s1 = 1;否则 $s1 = 0 比较是否小于常数;补码形式
无符号数比较小于时置位 sltu $s1, $s2, $s3 if($s2 < $s3) $s1 = 1;否则 $s1 = 0 比较是否小于;自然数
比较小于立即数时置位 sltiu $s1, $s2, 100 if($s2 < 100) $s1 = 1;否则 $s1 = 0 比较是否小于常数;自然数
无条件跳转 跳转 j 2500 跳至10000 跳转至目标地址
跳转至寄存器所指的位置 jr $ra 跳至$ra 用于switch语句,以及过程调用返回
跳转并链接 jal 2500 $ra = PC + 4;跳至10000 用于过程调用

2.2 计算机硬件的操作

  • 任何计算机必须能够执行算术运算。
add a,b,c # the sum of b and c is placed in a
  • 与其他汇编语言不同的是,这种语言的每一行最多只有一条指令;与C语言不同的是,注释总是在一行的末尾结束。
  • 要求每条指令有且仅有三个操作数,这一点符合硬件简单性的设计原则:操作数个数可变将给硬件设计带来更大的复杂性。

硬件设计基本原则:简单源于规整;越小越快;优秀的设计需要适宜的折中方案

2.3 计算机硬件的操作数

  • 与高级语言程序不同,MIPS算术运算指令的操作数必须来自寄存器。寄存器由硬件直接构建且数量有限,是计算机硬件设计的基本元素。
  • 高级语言的变量与寄存器的一个主要区别在于寄存器的数量有限。
  • MIPS约定书写指令时使用一个$ 符后面跟两个字符来代表一个寄存器。

2.3.1 存储器操作数

  • 数据结构(如数组和结构)是存放在存储器中。
  • MIPS的算术运算指令只对寄存器进行操作,故MIPS必须包含在存储器和寄存器之间传输数据的指令,称之为数据传送指令。为了访问存储器的一个字,指令必须给出储存器地址。
  • 将数据从存储器复制到寄存器的数据传送指令通常叫取数指令。取数指令的格式是操作码后接着目标寄存器,在后面是用来访问存储器的常数和寄存器。数据传输指令中的常量称为偏移量,存放基址的寄存器称为基址寄存器。
  • MIPS是按字节编址的,连续字的地址相差4,同时使用大端编址。
  • MIPS中偏移量和基址寄存器的寻址方式非常适合数组和结构,因为基址寄存器可以指向结构的首地址,偏移量可用于选择所需的数据元素。

2.3.2 常数或立即数操作数

  • 常数操作数出现频率高,而且相对从存储器中取常数,包含常数的算术运算指令执行速度快很多,并且能耗较低。
  • 由于MIPS支持负常数,故不需要设置减立即数的指令。

2.4 有符号数和无符号数

  • 所有信息都是由二进制数位或位组成,故二进制数运算基本单位是bit。
  • 最低有效位:在MIPS字中最右边的一位;
  • 最高有效位:在MIPS字中最左边的一位。

原码:

反码:

补码:

2.5 计算机的指令的表示

指令在计算机内部是以若干或高或低的电信号的序列表示的,并且形式上和数的表示相同。

  • 指令的布局形式叫做指令格式,为了将其与汇编语言区分开来,把指令的数字形式称为机器语言,其指令序列称为机器码。

MIPS字段

 1. R型(用于寄存器)

op(6位)

rs(5位)

rt(5位)

rd(5位)

shamt(5位)

funct(6位)

MIPS指令中各字段名称及含义如下:

  • op:指令的基本操作,通常称为操作码;
  • rs:第一个源操作数寄存器;
  • rt:第二个源操作数寄存器;
  • rd:用于存放操作结果的目的寄存器;
  • shamt:位移量;
  • funct:功能。用于指明op字段中操作的特定变式。

2. I型(用于立即数)

op(6位) rs(5位) rt(5位) constant or address(16位)
  •  16位的地址字段

2.6 逻辑操作

虽然早期的计算机仅对整字进行操作,但人们很快发现,对字中的若干位组成的字段甚至于对某个位进行操作是很有用的。

第一类逻辑操作成为移位;第二类有用的操作是按位与、按位或;最后一类逻辑操作是是按位取反

2.7 决策指令

计算机与简单计算器的区别在于决策能力

条件分支 

beq register1,register2,L1 # if register1 equal register2, then go to L1
bne register1,register2,L1

 案例分析

if(i == j)f = g + h;else f = g - h;

bne $s3,$s4,Else
add $s0,$s1,$s2 # f = g + h
j Exit
Else:sub $s0,$s1,$s2
Exit:
  • 在if语句的结尾部分,需要引入另一种分支指令,通常叫做无条件分支指令,当遇到这种指令时程序必须分支==>jump,简写为j。 

2.7.1 循环

2.7.2 case/switch语句

2.8 计算机硬件对过程的支持

过程或函数是程序员进行结构化编程的工具,两者均有助于提高程序的可理解性和代码的可重用性。

在过程运行中,程序必须遵循一下6个步骤:

  1. 将参数放在过程可以访问的位置;
  2. 将控制转交给过程;
  3. 获得过程所需的存储资源;
  4. 执行需要的任务;
  5. 将结果的值放在调用程序可以访问的位置;
  6. 将控制返回初始点,因为一个过程可能有一个程序中的多个点调用。 

MIPS软件在位过程调用分配了32个寄存器时遵循以下约定:

  • $a0 ~ $a3:用于传递参数的4个参数寄存器;
  • $v0 ~ $v1:用于返回值的两个值寄存器;
  • $ra:用于返回起始点的返回地址寄存器。

调转和链接指令:跳转到某个地址的同时将下一条指令的地址保存到寄存器 $ra中的指令;

返回地址:指向调用点的链接,使过程可以返回合适的地址,在MIPS中存储在寄存器$ra中。

为了避免保存和回复一个其值未被使用过的寄存器,MIPS将18个寄存器分为两组:

  1. $t0 ~ $t9:10个临时寄存器,在过程调用期间不必被调用者保存;
  2. $s0 ~ $s7:8个保留寄存器,在过程调用期间必须被保存;

2.8.1 使用更多的寄存器

  • 假设对一个过程,编译器需要使用多于4个参数寄存器和2个返回值寄存器,由于在任务完成后必须消除踪迹,因此调用者食用的任何寄存器都必须恢复到任务调用前所存储的值,可以看成需要将寄存器换出到存储器中。换出寄存器最理想的数据结构是栈(一种后进先出的队列),MIPS软件为栈指针预备了第29号寄存器,并命名为$sp。

案例分析

C:
int leaf(int g, int h, int i, int j)
{
    int f;
    f = (g+h)-(i+j);
    return f;
}
MIPS:
leaf:

addi $sp,$sp,-12
sw   $t1,8($sp);
sw   $t0,4($sp);
sw   $s0,0($sp);

add $t0,$a0,$a1 # g + h
add $t1,$a2,$a3 # i + j
sub $s0,$t0,$t1

add $v0,$s0,$zero

lw $s0,0($sp)
lw $t0,4($sp)
lw $t1,8($sp)

2.8.2 嵌套过程

2.8.3 在栈中为新数据分配空间

  • 栈需要存储过程的局部变量

2.8.4 在堆中为新数据分配空间

  • 存储常量和静态变量

2.9 人机交互

2.10 MIPS在32位立即数和寻址

2.10.1 32位立即数

MIPS指令集中的读取立即数高位指令lui专门用来设置寄存器中常数的高16位,ori用来插入低16位。

2.10.2 分支和跳转中的寻址

2.10.3 MIPS寻址模式总结

  • 立即数寻址
  • 寄存器寻址
  • 基址寻址
  • PC相对寻址
  • 伪直接寻址

2.11 并行与指令:同步

当任务之间相互独立的时候,任务的并行执行是比较容易的。但往往任务之间需要相互协作,这种协作通常意味着某些任务写的结果是其他任务需要读取的值。这时执行读任务的一方要知道写任务什么时候完成了写操作,才能安全地读回数据。就是说,任务之间需要同步(synchronize),否则就有发生数据竞争(data race)的危险,导致读数据错误而引起程序运行结果的改变。

数据竞争:假如来自不同线程的两个访存请求访问同一个地址,它们连续出现,并且至少其中一个是写操作,那么这两个存储访问形成数据竞争。

  • 加锁&解锁==>形成互斥。
  • 在多处理器中实现同步需要一组硬件原语,提供对存储单元进行原子读和原子写的能力,使得在进行存储器原子读或原子写操作时任何其他操作都不得插入。如果没有这样的硬件原语,那么建立同步机制的代价将会变得很高,并且随着处理器数量的增加情况将更为恶化。
  • 为了展示该原语建立同步原理的基本过程,假定使用存储器中某个单元来表示一个锁变|量:其数值为0时表示解锁,为1时表示加锁。一个处理器尝试对锁单元加锁,方法是用一个寄存器中的1与该锁单元的值进行交换。交换以后该锁单元的新值为1,返回值(锁单元的原值)如果是1,表明这个锁已被其他处理器占用;否则返回值为0,表示锁是自由的,尝试加锁成功。此时锁单元已被修改成1,以防止任何其他处理器再来占用。

2.12 翻译并执行程序

2.12.1 编译器

编译器将C程序转换成一种机器能理解的符号形式的汇编语言程序。

2.12.2 汇编器

汇编器将汇编语言指令转换成功能等价的机器语言。

2.12.3 链接器

链接器:也称链接编辑器。它是一个系统程序,把各个独立汇编的机器语言程序组合起来并且解决所有未定义的标记,最后生成可执行文件。

链接器的工作分3个步骤:

  1. 将代码和数据模块象征性地放入内存。
  2. 决定数据和指令标签的地址。
  3. 修补内部和外部引用。
  • 链接器产生一个可执行文件(executablefile),它可以在一台计算机上运行。通常,这个文件与目标文件具有相同的格式,但是它不包含未解决的引用。具有部分链接的文件是可能的,如库程序,在目标文件中仍含有未解决的地址。
  • 可执行文件:一个具有目标文件格式的功能程序,不包含未解决的引用。它可以包含符号表和调试信息。“剥离的可执行程序”不包含这些信息,可能包含加载器所需的重定位信息。

2.12.4 加载器

现在可执行文件已经在磁盘中,操作系统可以将其读入内存并启动执行它。在UNIX系统中,加载器(loader)按照如下步骤工作:

  1. 读取可执行文件头来确定代码段和数据段的大小。
  2. 为正文和数据创建一个足够大的地址空间。
  3. 将可执行文件中的指令和数据复制到内存中。
  4. 把主程序的参数(如果存在)复制到栈顶。
  5. 初始化机器寄存器,将栈指针指向第一个空位置。
  6. 跳转到启动例程,它将参数复制到参数寄存器并且调用程序的main函数。当main函数返回时,启动例程通过系统调用exit终止程序。

2.12.5 动态数据库

动态链接库:在程序执行过程中才被链接的库例程。

2.13 案例

2.14 数组与指针

你可能感兴趣的:(CS_Architecture)