计算机组成与设计(二)指令

指令集:一个给定的计算机体系结构所包含的指令集合,一种是人们编程书写的形式,另一种是计算机所能识别的形式。
共同目标:找到一种语言,可方便硬件和编译器的设计,且使性能最佳,同时使成本和功耗最低。

MIPS指令集:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
硬件设计原则
1:简单源于规整
例如:由高级语言转换成汇编语言
a=b+c;
d=a-e;

add a, b, c
sub d, a, e

2:越小越快(寄存器个数限制为32)
(1)大量的寄存器可能会使时钟周期变长
(2)受指令格式位数的限制
(在MIPS中可以用一个“$”符后面跟两个字符代表一个寄存器)
数据传送指令:存储器和寄存器之间传递数据的指令(load指令和store指令)
(将数据从存储器复制到寄存器的数据传送指令->load指令)
大端格式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中
小端格式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中
例如:假设变量h存放在寄存器 $s2中,数组A的基址放在 $s3中,编译c赋值语句
A[12]=h+A[8];

lw $t0,32($s3)
add $t0,$s2,$t0
sw $t0,48($s3)

寄存器与存储器相比,访问时间短、吞吐率高,寄存器中的数据访问速度快并易于利用,访问寄存器相对于访问存储器功耗更小。因此,为了获得高性能和节约功耗,指令集的体系结构必须拥有足够的寄存器,并且编译器必须高效地利用这些寄存器。

如果需要使用常数必须先将其从存储器中取出,例如,要使寄存器$s3加4

lw $t0,AddrConstant4($s1)
add $s3,$s3,$t0

或者使用加立即数的方法

addi $s3,$s3,4

常数0有效使用可以简化指令集,例如,数据传送指令正好可以被视为一个操作数0的加法,因此,MIPS将寄存器$zero至为0;(由于MIPS支持负常数,所以MIPS中不需要设置减立即数的指令)

表示有符号二进制数的方法(二进制补码)
在这里插入图片描述
采用二进制补码的优点在于所有负数最高有效位都是1,硬件只需检测这一位便可知正负,这个位也称为符号位
在这里插入图片描述
就正如无符号位会发生溢出一样,对于二进制补码的操作也可能发生溢出,溢出发生在符号位不正确(即负数符号位为0,正数符号位为1)

对于取数指令有符号数和无符号数是有区别的,取回有符号数需要使用符号位填充寄存器的所有剩余位,称为符号扩展;取回有符号位只需用0填充数据左侧的剩余位即可。
(1)对二进制补码取反,一个数和它按位取反的结果相加,和一定是-1
(2)将一个用n位表示的二进制数转化成一个用多于n位表示的数(符号扩展)

保持所有指令长度相0同,但不同类型的指令采用不同的指令格式
(R型,用于寄存器)
在这里插入图片描述
(I型,立即数)
在这里插入图片描述
在这里插入图片描述
将MIPS汇编语言翻译成机器语言
例如:数组A的基址存放在 $t1中,h存放在 $s2中
A[300] = h + A[300];
变成汇编语言

lw $t0,1200($t1)
add $t0,$s2,$t0
sw $t0,1200($t1)

在这里插入图片描述
在这里插入图片描述
1、指令用数的形式表示
2、和数据一样,程序存储在存储器中,并且可以读写
在这里插入图片描述
逻辑操作:用于简化对字中若干位进行打包或者拆包的操作
在这里插入图片描述
决策操作
1、beq(branch if equal)相等则分支
2、bne(branch if not equal)不相等则分支
3、无条件分支指令(jump)
4、slt(set on less than)小於则置位,若第一个寄存器小于第二个寄存器,则将第三个寄存器设置为1,否则设置为0
5、slti(立即数的小於则置位)
同样MIPS也提供处理无符号数的sltu指令和sltiu指令

例如:假设i和k存放在寄存器 $s3 和 $s5中,数组save的基址存放在寄存器 $s6中
while(save[i] == k)
i +=1;

//第一步需要将save[i]读入一个临时寄存器中,在读入之前需要计算它的地址,并增加一个标签lop,以便循环末端能够跳回该指令
loop:sll $t1,$s2,2//reg $t1=i*4
add $t1,$t1,$s6
lw $t0 ,0($t1) //将save[i]读入一个临时寄存器
bne $t0,$s5,Exit //如果save[i]不等于k退出循环
addi $s3,$s3,1
j Loop
Exit:

过程(根据提供的参数执行一定任务的存储的子程序),由于参数能够传递数值并返回结果,因此参数承担过程与其他程序、数据之间接口的角色。
(1)将参数放在过程可以访问的位置
(2)将控制转交给过程
(3)获得过程所需的存储资源
(4)执行需要的任务
(5)将结果的值放在调用程序可以访问的位置
(6)将控制返回初始点
在这里插入图片描述
跳转和链接指令(jalr):跳转到某个地址的同时将下一条指令的地址保存在寄存器 $ra中(返回地址)
寄存器跳转(jr):无条件跳转到寄存器所指定的地址

调用程序,将参数值放在 $a0- $a3,然后使用jal x 跳转到过程x,被调用者执行运算,将结果放在 $ v0 和 $v1,然后使用jr $ra指令将控制返回给调用者
在程序运行时,使用寄存器(程序计数器program counter)保存当前运行的指令地址;即jal指令实际上将PC+4保存在寄存器 $ra中,从而将链接指向下一条指令。

假设对于一个过程,编译器需要使用多于4个参数寄存器和两个返回值寄存器,“栈”是最理想的数据结构,需要一个指针指向栈中最新分配的地址,以指示下一个过程放置换出寄存器的位置;在每次寄存器进行保存或恢复时,栈指针(stack pointer)以字为单位进行调整(MIPS为栈指针准备了第29号寄存器, $sp,将数据放入栈中称为压栈,移除数据称为出栈)

栈的增长是按照地址从高到低的顺序进行的,意味着数据压栈时,栈指针减小;而数据出栈时,栈指针增大。

在这里插入图片描述
参数变量g、h、i、j对应参数寄存器$a0、 $a1、 $a2和 $a3,f对应 $s0
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
嵌套:将其他所有必须保留的寄存器压栈 或者 只需保证被调用者不在 $sp以上进行写操作即可

栈中包含过程所保存的寄存器和局部变量的片段称为过程帧(procedure frame)
在这里插入图片描述
堆为新数据分配空间:
在这里插入图片描述
内存低端的第一部分是保留的
程序代码(代码段)从地址0040 0000开始
静态数据从1000 0000开始
然后是动态数据,在C中使用malloc命令分配,在JAVA中使用new命令分配(动态数据在某一区域朝着栈的方向向上生长,称为堆)
全局指针 $gp应设置为适当地址以便于访问数据,初始化为1000 8000,这样相对于 $gp的正负16位的偏移量就可以访问从1000 0000至1000 ffff之间的内存空间。
栈指针初始化为7fff fffc,并向数据段的方向向下增长

C语言通过malloc函数在堆上分配空间并返回指向它的指针,free函数释放指针指向的堆空间;忘记释放空间会导致“内存泄漏”,会逐渐耗尽大量内存以至于操作系统可能崩溃,而过早释放空间会导致“悬摆指针”,会造成指针指向程序不想访问的位置。
JAVA使用自动的内存分配和无用单元回收机制来防止类似的错误发生。
在这里插入图片描述
人机交互:
大多数计算机使用8位的字节来表示字符,即ASCII码
在这里插入图片描述
表示一个字符串的方式有三种:
(1)保留字符串的第一个位置用于给出字符串的长度
(2)附加一个带有字符串长度的变量
(3)字符串最后的位置用一个字符来标识其结尾
例如,字符串“Cal”在C中用4字节表示
67、97、108、0

在这里插入图片描述

//假设数组x和y的基地址在$a0和$a1中,而i在$s0中,strcpy调整栈指针然后将保存的寄存器$s0保存在栈中
strcpy:
addi $sp,$sp,-4
sw $s0,0($sp)

add $s0,$zero,$zero
//循环的开始
L1:add $t1,$s0,$a1//address of y[i] in $t1
lbu $t2,0($t1) //$t2 = y[i]
//采用类似的方法将x[i]的地址放在$t3中,然后将$t2中的字符保存到该地址中
add $t3,$s0,$a0
sb $t2,0($t3)
//如果是0则退出循环
beq $t2,$zero,L2 //如果为0,则go to L2
addi $s0,$s0,1
j L1
L2:lw $s0,0($sp)
addi $sp,$sp,4
jr $ra 

JAVA使用unicode进行字符编码,默认使用16位来表示一个字符
在这里插入图片描述
MIPS中32位立即数和寻址
在这里插入图片描述

lui $s0,61//使用命令lui加载高16位
//执行完上面的质量$s0的值为0000 0000 0011 1101 0000 0000 0000 0000
//插入低16位,十进制表示是2304
ori $s0,$s0,2304

分支和跳转中的寻址
J型除了6位操作码之外,其余位都是地址字段
在这里插入图片描述
和跳转指令不同,条件分支指令除了规定分支地址之外还必须指定两个操作数
在这里插入图片描述
(PC相对寻址:使用PC来作为增加地址的寄存器,可转移到离当前指令距离为215个字的地方 )

MIPS对所有条件分支使用PC相对寻址,因为这些指令的跳转目标接近分支地址;另一方面,跳转链接指令并非总是靠近调用者的过程,所以它们通常使用其他寻址方式。即J型格式为过程调用提供长地址

在PC相对寻址所加的地址设计为字地址而非字节地址,16位的字地址相对于字节地址扩大了4倍;同样,26位字段也是字地址,表示为28位的字节地址。
在这里插入图片描述
在这里插入图片描述
如何使用远距离分支转移
在这里插入图片描述
在这里插入图片描述
(1)立即数寻址:操作数是位于指令自身中的常数
(2)寄存器寻址:操作数是寄存器
(3)基址寻址:操作数在内存中,其地址是指令中基址寄存器和常数的和
(4)PC相对寻址:地址是PC和指令中常数的和
(5)伪直接寻址:跳转地址由指令中26位字段和PC高位相连而成

任务之间需要同步(synchronize),否则就有发生数据竞争的危险,导致数据错误而引起程序运行结果的改变。
同步机制依赖硬件的同步指令,这些指令可由用户调用(加锁lock和解锁unlock同步操作),创建一个仅由单个处理器操作的区域,互斥区
在多处理器中实现同步需要一组硬件原语,提供对存储单元进行原子读和原子写的能力,使得在进行存储器原子读和原子写操作时任何其他操作都不能插入。

原子交换原语来演示如何建立基本同步机制(这个原语是将寄存器中的一个值和存储器中一个值相互交换)
假定使用存储器中某个单元来表示一个锁变量:0表示解锁,1表示上锁;一个处理器尝试对锁单元加锁,方法是用一个寄存器中的1与该锁单元的值进行交换;交换之后锁单元的新值为1,返回值如果是1,表明这个锁已经被其他处理器占用;否则返回值是0,表示锁是自由的,上锁成功。

实现单个的原子存储器操作需要存储器的读写操作都是有单条不可被中断的指令完成:
(1)指令对,其中第二条指令返回一个表明这对指令是否原子执行的标志值
(2)在MIPS中,这一指令对包括一条叫做链接取数(load linked)的特殊取数指令和叫做条件存数(store conditional)的特殊存数指令;如果链接取数指令所指定的锁单元的内容在相同的地址条件存数指令执行前被改变,那么条件存数指令就执行失败(条件存数指令:保存寄存器的值,并且如果执行成功将寄存器的值修改为1,否则修改为0;因为链接取数指令返回锁单元的原始值,条件存数指令执行成功才返回1)

在单处理器中,为了保证执行不被任何事件所干扰,条件存数指令在处理器两条指令之间进行上下文切换时也会失败

翻译并执行程序:
在这里插入图片描述
加载器的工作步骤:
1、读取可执行文件头来确定代码段和数据段的大小
2、为正文和数据创建一个足够大的地址空间
3、将可执行文件中的指令和数据复制到内存中
4、把主程序的参数复制到栈顶
5、初始化机器寄存器将栈指针指向第一个空位置
6、跳转到启动例程,将参数赋值到参数寄存器并且调用程序的main函数的,当main函数返回时,启动例程通过系统调用exit终止程序
在这里插入图片描述
UNIX系统中的目标文件通常包含以下6个不同的部分:
(1)目标文件头:描述目标文件其他部分的大小和位置
(2)代码段:包含机器语言代码
(3)静态数据段:包含在程序生命周期内分配的数据(静态数据存在于整个程序中;动态数据,随着程序的需要增长或减小)
(4)重定位信息:标记了一些在程序加载进内存时依赖于绝对地址的指令和数据
(5)符号表:包含未定义的剩余标记
(6)调试信息:包含一份说明目标模块如何编译的简明描述

动态链接库:由于静态方法虽然是最快的调用库程序的办法,但有以下缺点:

  • 库程序成为可执行代码的一部分,如果发布新版本的库(但程序中使用还是旧版本)
  • 在程序运行时,尽管可能不会使用库中的所有部分,但他们还是会被加载进来
    所以产生动态链接库(dynamically linked library),即在程序运行过程中才被链接的库例程

在这里插入图片描述
启动一个JAVA程序
在这里插入图片描述
从C程序手动翻译成汇编程序;
(1)为程序变量分配寄存器
(2)为过程体生成汇编代码
(3)保存过程调用间的寄存器
在这里插入图片描述
1、寄存器的分配与swap过程的第一部分变量的声明相对应
2、在这里插入图片描述
3、在这里插入图片描述
ARM与MIPS指令格式主要区别:
1、每条指令的4位条件执行字段不同
2、ARM因为只用MIPS一半数量的寄存器,所以具有相对较小的寄存器字段
在这里插入图片描述
在这里插入图片描述
寻址方式:
在这里插入图片描述
2、比较和条件分支
MIPS使用寄存器中的值来决定条件分支是否执行,而ARM使用传统的存储在程序状态字中的4位条件码来决定条件分支是否执行负值(negative)、零(zero)、进位(carry)和溢出(overflow)->这些设置功能是每条指令的可选功能,使用条件分支以判断所有有符号和无符号的关系。
在这里插入图片描述
ARM的特色:
1、由于没有专门的寄存器存储0,所以ARM需要单独的操作码来完成一些在MIPS中可以简单使用$zero来完成的操作
2、ARM支持多个字的算术操作
3、ARM的12位立即数字段:首先将右侧低8位的有效位填0扩展到32位,然后将所得的数循环右移,移动的位数由高4位的值乘以2决定(这种方式的优点在于可以在32位字的范围内表达所有2的幂次)
4、ARM还对寄存器组的操作提供了指令支持,这些指令叫做块(加载和存储),在指令的16位掩码的控制下,16个寄存器中的任意组合都可以被一条指令加载或存储到内存中,这些指令可以保存和恢复程序调用和返回时的寄存器
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章