5.1 ISA概述
ISA定义了软件编程所需要的必要而完整的描述,给出了内存组织方式、寄存器组、指令集(包括操作码、数据类型、寻址模式)等信息。
5.1.1 内存组织
LC-3的可寻址空间大小是(或65536),即地址线长度为16位,寻址(读写)基本单位是16位(数据线长度是16位)。我们把16位bit称为一个“字”,由于LC-3中数据处理的基本单位是16位,所以我们也称LC-3是一个“字寻址”机器。
5.1.2 寄存器
由于从内存中获取数据的速度很慢,所以LC-3还提供了临时存储空间。临时存储空间的最常见实现方式是寄存器。LC-3提供了一组通用寄存器,有时我们也称为寄存器文件。
5.1.3 指令集
指令集的定义包括操作码的集合、数据类型和寻址模式。
5.1.4 操作码
LC-3的ISA结构定义了15条指令,每条指令对应一个操作码(指令的bit[15:12],即指令的最左边四位)。在LC-3中,操作码值1101没有定义。
指令可以分为三类:
①运算指令:负责处理信息。
②数据搬移指令:负责在内存和寄存器以及内存/寄存器和输入/输出设备之间转移信息。
③控制指令:负责改变指令执行的顺序。
下图列出了LC-3的所有指令及其格式。
5.1.5 数据类型
数据类型是指信息的表达方式,即意味着ISA的操作码是怎么理解这些表达信息的。如果ISA的操作码能够识别/处理某种数据类型表示的信息,我们称该ISA支持这种数据类型。LC-3的ISA只支持补码整数这一种数据类型。
5.1.6 寻址模式
寻址模式是定义操作数位置(或来源)的一种机制。操作数可能存在的地方有:内存、寄存器和指令本身。其中,我们把存在指令本身的操作数称为“立即数”。所谓“立即数”就是指我们可以直接从指令本身获得该操作数,而不需要再从内存或寄存器中去寻找该操作数。
LC-3一共支持五种寻址模式:
①立即数
②寄存器
③相对寻址
④间接寻址
⑤基址偏移
5.1.7 条件码
LC-3能够利用条件码基于之前指令的执行结果来改变指令执行序列。LC-3中具有三个bit的寄存器,它们分别是:N、Z和P,对应的是负数、零、整数。我们把这三个单bit寄存器称为“条件码”。每当8个通用寄存器中任意一个被修改的时候,根据修改的结果,分别设置三个条件位。如果修改的结果是正数,则P位置1,N和Z清0;如果修改的结果是负数,则N位置1,Z和P清0;如果修改的结果是零,则Z位置1,N和P清0。
注意,条件码反应的是上一个被修改寄存器的修改结果。只有当寄存器的内容被修改时,条件码才有可能发生变化,因为修改前后的条件码可能一致。
5.2 操作指令
LC-3只支持三种操作指令:NOT、AND和ADD。
5.2.1 NOT指令
NOT指令(操作码=1101)是单操作数指令。NOT指令对16位源操作数“按位取反”,并将结果放入目的寄存器。NOT指令对源和目的寄存器的访问都是“寄存器寻址模式”。
NOT指令的格式如下:
NOT DR SR
1101 DR SR 111111
表示DR = = -SR-1(因为
)
下面是一个例子:
下面是数据通路中执行NOT指令的关键部分:
5.2.2 ADD指令
ADD指令执行两个操作数的补码加法。
ADD指令的格式如下:
①寄存器寻址模式
ADD DR SR1 SR2
0001 DR SR1 000 SR2
表示DR=SR1+SR2
②立即数寻址模式
ADD DR SR1 #立即数
0001 DR SR1 1 立即数(5位)
表示DR = SR1+立即数
下面是一个例子,其中下列指令存放在地址x4018中。
下面是数据通路中执行AND指令的关键部分:
由于立即数是5位,源操作数是16位,所以要先对立即数进行符号位拓展。
5.2.3 AND指令
AND指令对两个操作数的16个位中的每个bit进行“按位与”操作。
AND指令的格式如下:
①寄存器寻址模式
AND DR SR1 SR2
0101 DR SR1 000 SR2
表示DR = SR1 AND SR2
②立即数寻址模式
AND DR SR1 立即数
0101 DR SR1 1 立即数(5位)
表示DR = SR1 AND 立即数
5.2.4 其他
对数据清零的操作:AND SR SR #0
数据自增的操作:ADD SR SR #1
5.3 数据搬移指令
数据搬移指令是在通用寄存器和内存之间搬动数据。我们称数据从内存移入寄存器为“装载”(load),而从寄存器转向内存为“存储”(store)。注意,在两种情况下,源操作数都没有被改变,但目的操作数的内容被刷新了。
LC-3有7种搬移指令:LD、LDR、LDI、LEA、ST、STR和STI。
load和store指令的格式如下:
如果是load类型指令,则选择DR,表示指令结束后,来自内存的内容被写入DR寄存器;如果是store类型指令,则选择SR,表示指令结束后,SR寄存器的内容被写入内存。
数据搬移指令需要两个两个操作数:源和目的。源表示被搬移的数据,目的表示被搬移数据的去处。两个操作数中,一定有一个是寄存器,(在本章中)另一个一定是内存。
5.3.1 PC相对寻址
LD(opcode=1010)和ST(opcode=0011)采用PC相对寻址模式。计算方法是:先将bit[8:0]内容做16位扩展,然后与PC增量相加。注意,是与已经自增1的PC相加。如果是load操作,计算出来的地址就是将被读取的内存单元的地址,读取结果放在bit[11:9]制定的寄存器中。
下面是一个LD的例子,其中下列指令的地址是x4A1B,地址x49E8的内是x2110。
下面是该指令执行时的关键数据通路:
LD执行有三个步骤:
①将已增量PC的内容与IR[8:0]内容相加,得出的结果被装入MAR。PC是待执行的下一条指令的地址,IR是当前正在执行的指令的内容。即
MAR = PC + IR[8:0]
②把对应的数据装入MDR。即
MDR = M[MAR]
③将数据写入目的寄存器。即
R = MDR
LD指令可以总结为:R = M[PC+offset9]。
注意,PC相对寻址的范围是受限的。因为偏移量只有9位,所以偏移的地址只能在PC增量的+255和-256范围内。另外,指令中的偏移量是一个有符号数值。
该指令一共访问两次内存:读取指令、取操作数。
5.3.2 间接寻址
LDI和STI指令采用的是间接寻址模式。首先,采用跟LD和ST一样的方法计算出一个地址,但是该地址存放的并不是真正的操作数,而是操作数的地址。因此,我们称之为“间接”模式。
下面是一个LDI的例子:
下面是执行该指令相关的关键数据通路:
LDI执行有五个步骤:
①产生地址,将PC增量与IR[8:0]的符号扩展值相加,并将结果装入MAR寄存器。即
MAR = PC + IR[8:0]
②将MAR地址的内容读入MDR。即
MDR = M[MAR]
③把MDR存储的地址装入MAR。即
MAR = MDR
④把MAR地址的内容读入MDR。即
MDR = M[MAR]
⑤把MDR的内容装入目的寄存器。即
R = MDR
LDI指令可以总结为:R = M[M[PC+offset9]]。
LDI指令的可寻址范围是整个内存空间,一共访问三次内存。
5.3.3 基址偏移寻址
LDR和STR采用的是基址+偏移的寻址模式。基址寄存器是由指令的bit[8:6]指定的寄存器,而基址就是基址寄存器的内容,偏移就是指令的bit[5:0]字段。
下面是一个LDR的例子,R2的内容是x2345。
下面是执行该指令的关键数据通路。
LDR执行有三个步骤:
①将基址寄存器的内容与偏移量相加,将结果装入MAR。即
MAR = R + offset6
②将MAR地址的内容装入MDR。即
MDR= M[MAR]
③将MDR的内容装入目的寄存器。即
R = MDR
LDR指令可以总结为:R = M[base+offset6]。
LDR指令的可寻址范围也是整个内容空间,一共访问两次内存。
5.3.4 立即数寻址
LEA(opcode=1110)将增量PC和bit[8:0]的符号扩展值相加,并装入bit[11:9]指定的寄存器。LEA指令的用途是对寄存器做初始化,即向寄存器装入一个地址值。注意,装入目的寄存器的是地址,而不是某个地址里面存放的内容。
下面是一个例子,其中下列指令位于x4018处。
下面是执行LEA指令的关键数据通路。
LEA可总结为:R = PC+offset9。
除了取指令的时候需要访问内存,执行该指令的时候无需访问内存。
5.3.5 一个例子
下面是一个完整的例子。
具体的指令如下:
地址 | 指令 | 作用 |
x30F6 | LEA R1 -3 | R1 = PC-3 |
X30F7 | ADD R2 #14 | R2 = R1+14 |
X30F8 | ST R2 #-5 | M[X30F4]=R2 |
X30F9 | AND R2 #0 | R2 = 0 |
X30FA | ADD R2 R2 #5 | R2 = R2 + 5 |
X30FB | STR R2 R1 #14 | M[R1+14] = R2 |
X30FC | LDI R3 #-9 | R3 = M[M[X3F04]] |
5.4 控制指令
控制指令是指那些能够改变指令执行顺序的指令。如果没有控制指令,计算机只会按照内存位置的顺序,一条一条地执行指令。LC-3有5种控制指令:条件跳转、无条件跳转、子程序调用、TRAP、中断返回。
5.4.1 条件跳转指令
条件跳转指令(opcode=0000)的格式如下所示:
其中,bit[11]、[10]、[9]分别对应了之前所说的条件码。所有会对寄存器进行写操作的指令都会设置这三个条件码中的一个,这些指令包括:ADD、AND、NOT、LD、LDR、LDI、LEA。
条件跳转指令通过对条件码的判断,来决定是否改变指令执行的顺序。换句话说,如果条件跳转指令检查某个条件码(对应指令中指定的条件码)为1,则把PC增量和偏移量相加的结果存入PC;否则,PC不变。
条件跳转指令中的哪一位为0,就表示要检查哪一个条件码,如果指令中的N为1,就表示检查最近装入寄存器的内容是否为负数。如果三个位都为0,则表示一个条件码也不检查,PC内容不变;如果三个位都为1,则PC的内容一定会被修改,因为最近被装入寄存器的内容一定属于正负数和零中的某一个,我们称这种检查所有条件状态的指令为“无条件跳转”指令。
下面是一个例子,其中最近装入寄存器的内容是0,下列指令位于x4027处。
下面是执行该指令的关键数据通路。
BR指令可跳转的范围是有限的。
5.4.2 一个例子
下面是一个对12个整数求和的简单例子。
对应的指令如下:
地址 | 指令 | 作用 |
X3000 | LEA R1 X00FF | R1 = X3100 |
X3001 | AND R3 R3 #0 | R3 = 0 |
X3002 | AND R2 RE #0 | R2 =0 |
X3003 | ADD R2 R2 #12 | R2 = 12 |
X3004 |
|
|
X3005 | LDR R4 R1 #0 | R4 = M[R1] |
X3006 | ADD R3 R3 R4 | R3 = R3+R4 |
X3007 | ADD R1 R1 #1 | R1 = R1+1 |
X3008 | ADD R2 R2 #-1 | R2 = R2-1 |
X3009 |
|
|
5.4.3 循环控制的两种方法
控制循环体执行次数的方法有两种:
5.4.3.1 计数器方法
如果一个循环要执行n次,我们就将计数器初始化为n。然后每执行一次循环,就把计数器减1并判断其是否为0。如果计数器不为0,则重新设置PC至循环入口,然后开始下一轮循环。
5.4.3.2 哨兵法
在我们事先不知道要循环多少次的情况下特别有效。具体来说,在被处理的数据序列的尾部安放一个“哨兵”,即我们事先就知道绝对不会出现的数值。然后每次循环的时候通过判断哨兵是否出现来控制循环是否继续执行。一旦发现了哨兵,循环就该结束了。
5.4.3 例子:哨兵法数组求和
使用哨兵法对12个整数求和。
对应的指令如下:
地址 | 指令 | 作用 |
X3000 | LEA R1 X00FF | R1 = X3100 |
X3001 | AND R3 R3 #0 | R3 = 0 |
X3002 | LDR R4 R1 #0 | R4 = M[R1] |
X3003 |
|
|
X3004 | ADD R3 R3 R4 | R3 = R3 + R4 |
X3005 | ADD R1 R1 #1 | R1 = R1 +1 |
X3006 | LDR R4 R1 #0 | R4 = M[R1] |
X3007 |
|
|
5.4.4 JMP指令
JMP指令(opcode=1100)的格式如下:
JMP指令的任务是将寄存器的内容装入PC寄存器。因为寄存器的宽度是16-bit,可以表达内存空间的任意地址,所以JMP指令可以跳转到内容空间的任意位置。
5.4.5 TRAP指令
TRAP指令(opcode=1111)的任务是改变PC的内容,使其指向操作系统所在空间内部。换句话说,就是以当前程序的身份跳转到操作系统的某个代码入口开始执行,也就是让程序下一步执行特定的操作。
TRAP指令的格式如下:
其中,陷入矢量是程序希望操作系统执行的服务程序的编号。