1 指令概述
一条指令中应该有几个地址码字段?
- 零地址指令
(1) 无需操作数 如:空操作/停机等
(2) 所需操作数为默认的 如:堆栈/累加器等
形式:OP - 一地址指令
其地址既是操作数的地址,也是结果的地址
(1) 单目运算:如:取反/取负等
(2) 双目运算:另一操作数为默认的 如:累加器等
形式:OP+A1 - 二地址指令(最常用)
分别存放双目运算中两个操作数,并将其中一个地址作为结果的地址。
形式:OP+A1+A2 - 三地址指令(RISC风格)
分别作为双目运算中两个源操作数的地址和一个结果的地址。
形式:OP+A1+A2+A3 - 多地址指令
大中型机中用于成批数据处理的指令,如:向量 / 矩阵等(SIMD)
指令执行周期分为哪几个部分,各部分中指令分别涉及什么问题?
- Instruction Fetch:从存储器取指令;
涉及指令地址、指令长度(定长or变长) - Insruction Decode:对指令译码
涉及指令格式、操作码编码、操作数类型 - Operand Fetch:计算操作数地址并取操作数
涉及地址码、寻址方式、操作数格式和存放方式 - Execute:进行相应计算,并得到标志位
操作类型、标志或条件码 - Result Store:将计算结果保存到目的地
结果数据位置(目的操作数) - Next Instruction:计算下条指令地址(通常和取指令同时进行)
下条指令地址(顺序/转移)
2 操作数和寻址方式
操作数是指令处理的对象,与高级语言数据类型对应,基本类型有哪些?
- 地址(指针):被看作无符号整数,用来参加运算以确定主(虚)存地址
- 数值数据:
定点数(整数)---- 一般用二进制补码表示
浮点数(实数)---- 大多数机器采用IEEE754标准
十进制数 ---- 用NBCD码表示,压缩/非压缩(汇编程序设计时用) - 位、位串、字符和字符串:
用来表示文本、声音和图像等
4 bits is a nibble(一个十六进制数字)
8 bits is a byte
16 bits is a half-word
32 bits is a word - 逻辑(布尔)数据:
按位操作
什么是“寻址方式”?指令的寻址方式有哪些?操作数的寻址方式有哪些?
寻址方式就是根据地址找到指令或操作数的方法。(通常寻址方式特指“操作数的寻址”)
指令的寻址方式有:(1)PC增值;(2)跳转(jump、branch、call、return)同操作数的寻址
操作数的寻址方式有:
根据操作数的来源,有寄存器、外设端口、主(虚)存、栈顶
根据操作数结构,有位、字节、半字、字、双字、一维表、二维表
基本寻址方式的优缺点分别是什么?
方式 | 算法 | 主要优点 | 主要缺点 |
---|---|---|---|
立即 | 操作数=A | 指令执行速度快 | 操作数幅值有限 |
直接 | EA=A | 有效地址计算简单 | 地址范围有限 |
间接 | EA=(A) | 有效地址范围大 | 多次存储器访问 |
寄存器 | 操作数=(R) | 指令执行快,指令短 | 地址范围有限 |
寄间接 | EA=(R) | 地址范围大 | 额外存储器访问 |
偏移 | EA=A+(R) | 灵活 | 复杂 |
堆栈 | EA=栈顶 | 指令短 | 应用有限 |
偏移寻址方式有哪些?
-
相对寻址: EA=A+(PC) 相对于当前指令处位移量为A的单元
指令地址码给出一个偏移量(带符号数),基准地址隐含由PC给出。
特点:可用来实现程序(公共子程序)的浮动 或 指定转移目标地址
注意:当前PC的值可以是正在执行指令的地址或下条指令的地址
-
基址寻址: EA=A+(B) 相对于基址(B)处位移量为A的单元
指令地址码给出一个偏移量,基准地址明显或隐含由基址寄存器B给出。
特点:可用来实现多道程序重定位 或 过程调用中参数的访问 -
变址寻址: EA=A+(I) 相对于首址A处位移量为(I)的单元
指令地址码给出一个基准地址,而偏移量(无符号数)明显或隐含由变址寄存器 I 给出。
特点:可为循环重复操作提供一种高效机制,如实现对线性表的方便操作
注意:一般RSIC机器不提供自动变址寻址,并将变址和基址寻址统一成一种偏移寻址方式
若转移指令地址为2000H,转移目标地址为1FF0H,总是在取指令同时对PC增量,则转移指令第二字节位移量为多少?
不知道。
因为只有确定了是按字还是按字节编址、位移量D是指指令条数还是单元数,才能确定目标地址范围(目标地址范围不等于位移量D的表示范围)。
当按字节编址且D为单元数时,转移目标地址= (PC)+2+D
跳转范围:-126~128单元;-63~64条指令
1FF0H = 2000H+2+D
D=1FF0H–2002H=EEH(–18)
举例:MIPS指令“beq $1, $2, 25”的转移目标地址为(PC)+4+4*25,这里的25是指令条数而不是单元数,MIPS采用定长指令字,按字节编址, 所有指令的长度都是32位(4字节)。
循环处理的汇编表示
for (i=0;i<N;i++)
g = g + A[i];
Assuming variables i, g ~ $7, $8 and base address of array is in $9
add $7, $0, $0 // i=0
Loop: add $10, $7, $9 // $10=&A[i]
lw $6, 0($10) // $6=A[i]
add $8, $8, $6 // g= g+A[i]
addi $7, $7, 4 // i=i+1
bne $7, $2, Loop
3 指令风格
按指令格式的复杂度来分,有两种类型计算机:
复杂指令集计算机CISC (Complex Instruction Set Computer)
精简指令集计算机RISC (Reduce Instruction Set Computer)
早期CISC设计风格的主要特点有哪些?
(1) 指令系统复杂
变长操作码 / 变长指令字 / 指令多 / 寻址方式多 / 指令格式多
(2) 指令周期长
绝大多数指令需要多个时钟周期才能完成
(3) 各种指令都能访问存储器
除了专门的存储器读写指令外,运算指令也能访问存储器
(4) 采用微程序控制
(5) 有专用寄存器
(6) 难以进行编译优化来生成高效目标代码
CISC的缺陷
日趋庞大的指令系统不但使计算机的研制周期变长,而且难以保证设计的正确性,难以调试和维护,并且因指令操作复杂而增加机器周期,从而降低了系统性能。在程序中各种指令出现的频率悬殊很大,最常使用的是一些简单指令,这些指令占程序的80%,但只占指令系统的20%。而且在微程序控制的计算机中,占指令总数20%的复杂指令占用了控制存储器容量的80%。
RISC设计风格的主要特点
(1) 简化的指令系统
指令少 / 寻址方式少 / 指令格式少 / 指令长度一致
(2) 以RR方式工作
除Load/Store指令可访问存储器外,其余指令都只访问寄存器。
(3) 指令周期短
以流水线方式工作, 因而除Load/Store指令外,其他简单指令都只需一个或一个不到的时钟周期就可完成。
(4) 采用大量通用寄存器,以减少访存次数
(5) 采用组合逻辑电路控制,不用或少用微程序控制
(6) 采用优化的编译系统,力求有效地支持高级语言程序
4 MIPS指令格式
所有指令都是32位宽,须按字地址对齐,且字地址为4的倍数。
MIPS的指令格式有哪三种?
-
R-Type
两个操作数和结果都在寄存器的运算指令。如:sub rd, rs, rt -
I-Type
运算指令:一个寄存器、一个立即数。如:ori rt, rs, imm16
LOAD和STORE指令。如:lw rt, rs, imm16
条件分支指令。如:beq rs, rt, imm16 -
J-Type
无条件跳转指令。如:j target
MIPS的call、return、jump、branch和compare指令的区别和联系
Instruction | Example | Meaning |
---|---|---|
jump register | jr $31 | go to $31 //For switch, procedure return (对应过程返回) |
jump and link | jal 10000 | $31 = PC + 4; go to 10000 //For procedure call(对应过程或函数调用) |
jump | j 10000 | go to 10000 // Jump to target address |
slt | slt $1,$2,$3 | set on less than if ($2 < $3) $1=1; else $1=0 |
slti | slti $1,$2,100 | set less than imm. if ($2 < 100) $1=1; else $1=0 |
beq | beq $1,$2,100 | branch on equal if ($1 == $2) go to PC+4+25*4 |
bne | bne $1,$2,100 | branch on not eq. if ($1!= $2) go to PC+4+25*4 |
注意:分支指令的机器代码中给出的是相对于当前指令的指令条数
if-then-else语句和“=”判断的MIPS汇编语言表示
if (i = = j)
f = g+h ;
else
f = g-h ;
Assuming variables i, j, f, g, h, ~ $1, $2, $3, $4, $5
bne $1, $2, else //i!=j, jump to else
add $3, $4, $5
j exit //jump to exit
else: sub $3, $4, $5
exit:
“less than”判断的MIPS汇编语言表示
if (a < b) f = g+h ; else f = g-h ;
Assuming variables a, b, f, g, h, ~ $1, $2, $3, $4, $5
slt $6, $1, $2 // if a<b, $6=1, else $6=0
beq $6, $zero, else // $6=0, jump to else
add $3, $4, $5
j exit //jump to exit
else: sub $3, $4, $5
exit:
Loop循环的MIPS汇编语言表示
Loop: g = g +A[i];
i = i+ j;
if (i != h) go to Loop:
Assuming variables g, h, i, j ~ $1, $2, $3, $4 and base address
of array is in $5
Loop: add $7, $3, $3 // i*2
add $7, $7, $7 //i*4
add $7, $7, $5
lw $6, 0($7) //$6=A[i]
add $1, $1, $6 //g= g+A[i]
add $3, $3, $4
bne $3, $2, Loop
5 过程调用和虚拟地址
过程调用的执行步骤(过程P调用过程Q)是怎么样的?
- 将参数放到Q能访问到的地方
- 将P中的返回地址存到特定的地方,将控制转移到过程Q(前两次都是在调用过程P中完成)
- 为Q的局部变量分配空间(局部变量临时保存在栈中)
- 执行过程Q
- 将Q执行的返回结果放到P能访问到的地方
- 取出返回地址,将控制转移到P,即返回到P中执行(后四次在被调用过程Q中完成)
过程调用时的变量是怎么分配的?
为减少指令条数,并减少访问内存次数,在每个过程的过程体中总是先使用临时寄存器
t
0
t0~
t0 t9;临时寄存器不够或者某个值在调用过程返回后还需要用,就使用保存寄存器
s
0
s0~
s0 s7
一个程序在“编辑、编译、汇编、链接、装入”过程中的哪个环节确定了每条指令及其操作数的虚拟地址?
链接时确定虚拟地址;装入时生成页表以建立虚拟地址与物理地址之间的映射!
加载时是否真正从磁盘调入信息到主存?
实际上不会从磁盘调入,只是将代码和数据与虚拟空间建立对应关系,称为“映射”。
6 字符串
使用一串ASCII码而不用整数来表示数字。如果用ASCII码表示10亿这个数将用32位整数表示增加多少存储?
10亿就是10的9次方,需要使用10位ASCII码表示,每一个ASCII码都是8位长。所以存储将增长到(10*8)/32,即2.5倍。
除了存储空间要增加外,用于对这些十进制数字进行加、减、乘、除的硬件设计也是困难的。这些困难解释了为什么计算专家越来越相信使用二进制的计算机是自然的,而偶然出现的十进制计算机则是奇怪的。
通过编译一个字符串复制过程,来展示如何使用C字符串:
strcpy过程将C语言中约定使用null字节结束的字符串y复制到字符串x,下列代码编译后的MIPS汇编代码是怎么样的?
void strcpy(char x[],char y[])
{
int i;
i = 0;
while ((x[i]=y[i])!='\0')
i += 1;
}
假设数组x和y的基地址在$a0 和 $a1中,而 i 在 $s0中。
(1)strcpy调整栈指针然后将保存的寄存器 $s0 保存在栈中:
strcpy:
addi $sp,$sp,-4
sw $s0,0($sp)
(2)将i初始化为0:
add $s0,$zero,$zero
(3)循环的开始:
L1: add $t1,$s0,$a1 // address of y[i] in $t1
(4)为读取y[i],使用无字符字节(y是字节的数组而非字的数组)读取指令,将字符放在$t2中:
lbu $t2,0($t1) // $t2 = y[i]
(5)采用类似的计算方式将x[i]的地址放在$t3 中,然后将 $t2 中的字符保存到该地址中:
add $t3,$s0,$a0 //address of x[i] in $t3
sb $t2,0($t3) //x[i]=y[i]
(6)如果字符是0则退出循环,如果是字符串的最后一个字符则退出:
beq $t2,$zero,L2 //if y[i]==0,go to L2
(7)如果不是,将i加1继续循环:
addi $s0,$s0,1 //i=i+1
j L1 //go to L1
(8)如果不继续循环,那就是到了字符串的最后一个字符,我们还原 $s0 和栈指针,然后返回:
L2: lw $s0,0($sp)
addi $sp,$sp,4
jr $ra