====== 第六章 指令系统 ====== ===== 6.1 指令系统概述 ===== ==== 6.1.1 指令系统的基本概念 ==== 指令系统是计算机硬件能够识别和执行的全部指令的集合,它是计算机硬件与软件之间的接口,也是计算机系统结构的重要组成部分。指令系统的完备性、规整性和高效性直接影响着计算机系统的性能、成本和适用范围。 从程序员的角度看,指令系统定义了计算机的"语言",程序员通过指令系统中的指令来控制计算机完成各种操作。从硬件设计者的角度看,指令系统规定了CPU必须实现的功能,是CPU设计的依据。 一个完整的指令系统应该包含以下基本类型的指令: - **数据传送指令**:在寄存器、存储器和I/O设备之间传送数据 - **算术运算指令**:执行加、减、乘、除等算术运算 - **逻辑运算指令**:执行与、或、非、异或等逻辑运算 - **移位指令**:实现数据的左移、右移、循环移位等操作 - **转移指令**:改变程序执行的顺序,包括无条件转移、条件转移等 - **子程序调用与返回指令**:支持程序的模块化设计 - **输入输出指令**:实现与外部设备的数据交换 - **特权指令**:用于操作系统管理,一般在用户态下不能执行 ==== 6.1.2 指令系统的设计原则 ==== 设计一个优秀的指令系统需要遵循以下原则: **完备性**:指令系统应该提供足够的指令来完成各种计算任务。完备性并不意味着指令越多越好,而是指令功能要齐全,能够支持高级语言的各种数据类型和控制结构。 **有效性**:指令系统应该使编译生成的目标代码短小、执行速度快。这需要指令系统设计考虑到编译技术的特点,便于编译器生成高效的代码。 **规整性**:指令系统的设计应该遵循统一的规则,包括对称性、均匀性和一致性。例如,所有通用寄存器应该具有相同的功能,指令格式应该统一等。 **兼容性**:新的计算机系统应该能够运行旧系统开发的软件,以保证软件资源的继承性。这在商业计算机系统的设计中尤为重要。 ==== 6.1.3 指令系统的分类 ==== 根据指令系统的复杂程度,可以将计算机分为以下几类: **CISC(复杂指令集计算机)**:指令系统庞大,指令格式复杂,寻址方式多样。典型代表有Intel x86系列处理器。CISC的设计目标是用较少的指令完成更多的工作,减少程序代码的长度。 **RISC(精简指令集计算机)**:指令系统精简,指令格式规整,大多数指令在一个时钟周期内完成。典型代表有MIPS、ARM、RISC-V等处理器。RISC的设计目标是通过简化指令系统来提高指令执行速度。 **VLIW(超长指令字计算机)**:将多条独立的指令组合成一个超长指令字,同时执行。EPIC(显式并行指令计算)是VLIW的一种发展。 ===== 6.2 指令格式 ===== ==== 6.2.1 指令的基本格式 ==== 一条指令通常由两部分组成:操作码(Opcode)和地址码(Address Field)。 **操作码**:指明指令要执行的操作类型,如加、减、传送、转移等。操作码的位数决定了指令系统最多可以包含的指令条数。如果有n位操作码,则最多可以表示2^n条不同的指令。 **图6-1:指令的基本格式** {{https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/Instruction_format.svg/800px-Instruction_format.svg.png?direct|指令格式示意图|500}} **地址码**:指明操作数的地址或操作数本身。根据地址码的数量,指令可以分为以下几种格式: **零地址指令**:只有操作码,没有地址码。这类指令的操作数是隐含的,通常是堆栈顶部的数据。例如,零地址加法指令"ADD"将堆栈顶部的两个数弹出,相加后将结果压入堆栈。 **一地址指令**:包含一个地址码。这类指令通常用于单操作数运算或累加器型运算。例如,一地址加法指令"ADD X"表示将累加器的内容与存储单元X的内容相加,结果存入累加器。 **二地址指令**:包含两个地址码。格式为:OP A1, A2,表示将A1和A2的内容进行操作,结果存入A1(或A2)。例如,"ADD R1, R2"表示将寄存器R1和R2的内容相加,结果存入R1。 **三地址指令**:包含三个地址码。格式为:OP A1, A2, A3,表示将A1和A2的内容进行操作,结果存入A3。例如,"ADD R1, R2, R3"表示将寄存器R1和R2的内容相加,结果存入R3。 **四地址指令**:包含四个地址码,除了三个操作数地址外,还包含下一条指令的地址。这种格式在现代计算机中很少使用,因为下一条指令的地址通常由程序计数器PC自动提供。 ==== 6.2.2 指令字长度 ==== 指令字长度是指一条指令所占的二进制位数。根据指令字长度与机器字长的关系,可以分为: **单字长指令**:指令长度等于机器字长。这种指令只需一次访存就可以取出,执行速度较快。 **半字长指令**:指令长度等于半个机器字长。一条指令可以存放在半个存储字中,提高了存储空间的利用率。 **双字长指令**:指令长度等于两个机器字长。这种指令可以包含更多的信息,如更大的立即数或更远的地址,但需要两次访存才能取出。 ==== 6.2.3 操作码扩展技术 ==== 为了在保证指令系统功能的前提下缩短指令长度,可以采用操作码扩展技术。基本思想是:对使用频率高的指令分配较短的操作码,对使用频率低的指令分配较长的操作码。 **Huffman编码原理**:根据指令使用频率进行编码,使用频率高的指令用短码表示,使用频率低的指令用长码表示,从而使平均码长最短。 **扩展操作码法**:指令操作码的位数不固定,通过设定标志位来区分不同长度的操作码。例如,15条三地址指令可以用4位操作码表示(0000-1110),留下1111作为扩展标志,表示后续还有4位操作码,可以表示16条二地址指令。 ===== 6.3 寻址方式 ===== ==== 6.3.1 寻址方式概述 ==== 寻址方式是指确定指令中操作数地址的方法。丰富的寻址方式可以提高编程的灵活性,但也会增加硬件设计的复杂性和指令执行的时间。 ==== 6.3.2 常见的寻址方式 ==== **立即寻址(Immediate Addressing)**:操作数直接包含在指令中。例如,"MOV AX, 100H"将立即数100H送入AX寄存器。这种寻址方式不需要访存,执行速度快,但操作数的范围受限于指令中地址字段的位数。 **直接寻址(Direct Addressing)**:指令中的地址字段直接给出操作数在存储器中的有效地址。例如,"MOV AX, [1000H]"将存储单元1000H中的内容送入AX寄存器。这种寻址方式简单直观,但寻址范围有限。 **间接寻址(Indirect Addressing)**:指令中的地址字段给出的是操作数有效地址的地址。例如,"MOV AX, @1000H"表示存储单元1000H中的内容是操作数的有效地址。这种寻址方式可以扩大寻址范围,但需要多次访存。 **寄存器寻址(Register Addressing)**:操作数在CPU的通用寄存器中。例如,"MOV AX, BX"将BX寄存器的内容送入AX寄存器。这种寻址方式不需要访存,执行速度最快。 **寄存器间接寻址(Register Indirect Addressing)**:寄存器中的内容是操作数的有效地址。例如,"MOV AX, [BX]"将BX寄存器内容所指存储单元的内容送入AX寄存器。 **基址寻址(Base Addressing)**:操作数的有效地址等于基址寄存器的内容加上指令中的形式地址。这种寻址方式常用于程序的重定位和多道程序设计。 **变址寻址(Indexed Addressing)**:操作数的有效地址等于变址寄存器的内容加上指令中的形式地址。这种寻址方式特别适合数组访问和循环程序设计。 **相对寻址(Relative Addressing)**:操作数的有效地址等于程序计数器PC的内容加上指令中的形式地址(偏移量)。这种寻址方式主要用于转移指令,使程序可以在内存的任何位置运行。 **堆栈寻址(Stack Addressing)**:操作数在堆栈中,由堆栈指针SP自动管理。堆栈操作遵循"后进先出"(LIFO)的原则。 ==== 6.3.3 寻址方式举例分析 ==== 【例题1】某计算机字长为16位,存储器按字节编址,PC当前值为1000H。若采用相对寻址,指令为双字节,偏移量为05H(用补码表示),求目标地址。 解:由于存储器按字节编址,取出双字节指令后,PC的值变为1002H。 目标地址 = PC + 偏移量 = 1002H + 05H = 1007H 【例题2】某计算机指令字长为16位,地址码为6位,采用扩展操作码技术。若已有15条三地址指令,8条二地址指令,问还能设计多少条一地址指令? 解:操作码共4位(16-6-6=4位表示操作码) - 三地址指令:4位操作码,可表示16条,用了15条,余1种编码用于扩展 - 二地址指令:扩展4位,共8位操作码,可表示16条,用了8条,余8种编码用于扩展 - 一地址指令:扩展6位,共14位操作码,8×64=512条 答:还能设计512条一地址指令。 ===== 6.4 指令类型 ===== ==== 6.4.1 数据传送指令 ==== 数据传送指令是最基本的指令类型,用于在寄存器、存储器和I/O端口之间传送数据。这类指令通常不改变操作数的值,只是改变数据的位置。 **通用数据传送指令**: - MOV:数据传送,将源操作数传送到目的操作数 - PUSH/POP:堆栈操作,将数据压入或弹出堆栈 - XCHG:交换两个操作数的值 - XLAT:查表转换,用于代码转换 **地址传送指令**: - LEA:装入有效地址,将源操作数的有效地址送入寄存器 - LDS/LES:装入段地址和偏移地址 **标志传送指令**: - LAHF/SAHF:标志寄存器与AH寄存器之间的传送 - PUSHF/POPF:标志寄存器与堆栈之间的传送 ==== 6.4.2 算术运算指令 ==== 算术运算指令用于执行各种算术运算,包括加、减、乘、除以及相关的辅助操作。 **加法指令**: - ADD:普通加法 - ADC:带进位加法,用于多字节加法 - INC:加1指令 - AAA/DAA:ASCII/十进制加法调整 **减法指令**: - SUB:普通减法 - SBB:带借位减法,用于多字节减法 - DEC:减1指令 - NEG:取负指令 - CMP:比较指令,执行减法但不保存结果 **乘法指令**: - MUL:无符号乘法 - IMUL:带符号乘法 **除法指令**: - DIV:无符号除法 - IDIV:带符号除法 ==== 6.4.3 逻辑运算与移位指令 ==== **逻辑运算指令**: - AND:逻辑与,用于屏蔽某些位 - OR:逻辑或,用于设置某些位为1 - XOR:逻辑异或,用于求反或清零 - NOT:逻辑非,取反操作 - TEST:测试指令,执行AND操作但不保存结果 **移位指令**: - SHL/SHR:逻辑左移/右移,空位补0 - SAL/SAR:算术左移/右移,左移补0,右移补符号位 - ROL/ROR:循环左移/右移 - RCL/RCR:带进位循环左移/右移 ==== 6.4.4 控制转移指令 ==== 控制转移指令用于改变程序执行的顺序,是程序实现分支、循环和子程序的基础。 **无条件转移指令**: - JMP:无条件跳转到目标地址 **条件转移指令**:根据标志寄存器的状态决定是否转移 - JE/JZ:等于/为零则转移 - JNE/JNZ:不等于/非零则转移 - JA/JNBE:高于/不低于等于则转移(无符号) - JG/JNLE:大于/不小于等于则转移(有符号) - JB/JNAE:低于/不高于等于则转移(无符号) - JL/JNGE:小于/不大于等于则转移(有符号) **子程序调用与返回指令**: - CALL:调用子程序,将返回地址压入堆栈 - RET:从子程序返回,从堆栈弹出返回地址 **中断指令**: - INT:软中断指令 - IRET:中断返回指令 ===== 6.5 RISC与CISC的比较 ===== ==== 6.5.1 CISC的特点 ==== **设计目标**:用较少的指令完成更多的工作,减少程序代码长度,节省存储空间。 **主要特点**: - 指令系统庞大,指令条数多(通常超过200条) - 指令格式复杂,长度可变 - 寻址方式多样(通常超过10种) - 指令执行周期长,大多数指令需要多个时钟周期 - 大量使用微程序控制 - 有专门的复杂指令,如字符串处理、十进制运算等 **优点**: - 程序代码密度高,节省存储空间 - 对高级语言的支持较好,某些指令直接对应高级语言的操作 - 与早期计算机兼容性好 **缺点**: - 指令执行速度慢 - 硬件设计复杂,成本高 - 大量使用微程序控制,效率低 - 复杂指令的使用频率低 ==== 6.5.2 RISC的特点 ==== **设计目标**:通过简化指令系统来提高指令执行速度,使大多数指令在一个时钟周期内完成。 **主要特点**: - 指令系统精简,指令条数少(通常少于100条) - 指令格式规整,长度固定 - 寻址方式简单(通常少于5种) - 大多数指令在一个时钟周期内完成 - 采用硬布线控制,提高执行速度 - 大量使用寄存器,减少访存次数 - 只有Load/Store指令可以访存 **优点**: - 指令执行速度快 - 硬件设计简单,成本低 - 便于流水线操作,提高并行性 - 编译器优化更容易 **缺点**: - 程序代码密度低,需要更多存储空间 - 某些操作需要多条指令实现 - 对编译器的要求较高 ==== 6.5.3 RISC与CISC的比较总结 ==== | 特性 | CISC | RISC | |------|------|------| | 指令条数 | 多(>200) | 少(<100) | | 指令长度 | 可变 | 固定 | | 寻址方式 | 多样 | 简单 | | 执行周期 | 不固定,多数为多周期 | 单周期 | | 控制方式 | 微程序控制为主 | 硬布线控制为主 | | 访存指令 | 不限于Load/Store | 只有Load/Store | | 寄存器数量 | 较少 | 较多 | | 代码密度 | 高 | 低 | | 设计复杂度 | 硬件复杂 | 编译器复杂 | | 典型代表 | x86 | MIPS、ARM、RISC-V | ==== 6.5.4 现代处理器的发展趋势 ==== 现代处理器设计吸收了RISC和CISC各自的优点: **x86处理器的RISC化**:现代x86处理器(如Intel Core系列、AMD Ryzen系列)在内部将CISC指令翻译成类RISC的微操作(μops),然后采用RISC技术执行。 **RISC处理器的CISC化**:一些RISC处理器增加了指令系统的复杂性,以支持更多的应用需求。例如,ARM处理器从ARMv7开始支持Thumb-2技术,将16位和32位指令混合使用。 **融合趋势**:现代处理器设计不再严格区分RISC和CISC,而是根据应用需求选择最合适的技术。 ===== 6.6 典型指令系统实例 ===== ==== 6.6.1 MIPS指令系统 ==== MIPS(Microprocessor without Interlocked Pipeline Stages)是典型的RISC处理器,其指令系统设计简洁规整,非常适合教学和研究。 **MIPS指令格式**: - R型(寄存器型):用于寄存器到寄存器的操作 - I型(立即数型):用于带立即数的操作和Load/Store - J型(跳转型):用于无条件跳转 **MIPS常用指令**: - 算术运算:add, sub, addi, addu, subu - 逻辑运算:and, or, xor, nor, andi, ori - 移位运算:sll, srl, sra, sllv, srlv - 数据传送:lw, sw, lb, sb, lui - 条件分支:beq, bne, slt, slti - 无条件跳转:j, jal, jr ==== 6.6.2 ARM指令系统 ==== ARM(Advanced RISC Machines)是目前使用最广泛的嵌入式处理器架构,其指令系统具有以下特点: **指令特点**: - 所有指令都是32位(ARM状态)或16位(Thumb状态) - 大多数指令可以条件执行 - 支持多种数据类型(字节、半字、字、双字) - 具有桶形移位器,可以在指令中完成移位操作 **指令分类**: - 数据处理指令 - Load/Store指令 - 分支指令 - 协处理器指令 - 杂项指令 ==== 6.6.3 x86指令系统 ==== x86是Intel开发的CISC指令系统,是目前个人计算机和服务器上使用最广泛的架构。 **指令特点**: - 指令长度可变(1-15字节) - 寻址方式灵活多样 - 具有丰富的指令类型 - 向后兼容性好(可执行30年前的程序) **指令格式**:前缀 + 操作码 + ModR/M + SIB + 位移量 + 立即数 ===== 6.7 本章重点总结 ===== **核心概念**: - 指令系统是计算机硬件与软件的接口,决定了计算机的基本功能 - 指令格式包括操作码和地址码,根据地址码数量可分为零地址到四地址指令 - 寻址方式确定操作数的位置,常见寻址方式包括立即、直接、间接、寄存器、基址、变址、相对等 **关键技术**: - 操作码扩展技术:通过Huffman编码或扩展操作码来缩短指令平均长度 - RISC设计思想:精简指令系统,规整指令格式,提高执行速度 - 流水线友好设计:规整的指令格式便于流水线高效执行 **重要对比**: - CISC vs RISC:CISC指令复杂、功能强、代码密度高;RISC指令简单、执行快、易流水线化 - 现代处理器融合了两者的优点,取长补短 **典型实例**: - MIPS:经典的教学用RISC处理器,指令格式规整 - ARM:广泛使用的嵌入式处理器,支持条件执行 - x86:主流的CISC处理器,向后兼容性好 **学习建议**: - 理解各种寻址方式的原理和适用场景 - 掌握指令格式设计的基本原则 - 能够进行简单的指令格式设计和操作码扩展计算 - 了解不同指令系统架构的特点和适用领域