CO——指令系统

5.1 指令系统的基本概念

5.1.1 机器语言、机器指令与指令系统

机器语言是计算机唯一可以识别的语言,本质是一串二进制代码,机器语言由一条条机器指令构成。

  • 机器指令:机器语言的单条语句,用于告诉计算机完成一项操作。

  • 指令系统:一台计算机中全部机器指令的集合。

5.1.2 指令格式

一条指令由两大字段构成:

|--------------|-------------|
| 操作码字段 OP | 地址码字段 A |

  • 操作码 OP :表征指令要完成的操作性质与功能。分为定长操作码变长(不定长)操作码。定长操作码的位数直接决定指令系统最多支持的指令种类。

  • 地址码 A:存放操作相关地址信息,可以是立即数、寄存器编号、主存地址、I/O 设备地址。

各类地址指令

|----|----|----|----|----|
| OP | A1 | A2 | A3 | A4 |

  1. 四地址指令:OP A₁ A₂ A₃ A₄。A₁、A₂为源操作数地址;A₃存放结果;A₄存放下一条指令地址。

  2. 三地址指令:OP A₁ A₂ A₃。去掉 A₄,下一条指令地址由 PC 自增得到。

  3. 二地址指令:OP A₁ A₂。省略结果地址 A₃,运算结果写回 A₁;也可写回累加器 ACC;PC 自增。

  4. 一地址指令:OP A₁。一个源操作数隐含在 ACC 中,运算结果写回 ACC;PC 自增。例子:INC 自增指令。

  5. 零地址指令:只有操作码,无地址字段。

    1. 一类:无操作数,如空操作、停机、关中断指令。

    2. 一类:操作数隐含,堆栈指令 PUSH、POP,操作数由堆栈指针 SP 隐含给出。

5.1.3 扩展操作码技术

指令字长固定,把部分地址码的位数拿出来分配给操作码,以此实现变长操作码,在有限指令字长下扩充指令的数量。

5.1.4 指令的操作类型

  • 数据传送指令

    • MOV:源操作数传送至目的位置,源操作数保持不变;

    • LOAD:主存→寄存器;STORE:寄存器→主存;

    • EXC:交换源、目的位置内容;

    • PUSH 进栈、POP 出栈。

  • 算术运算指令:加、减、乘、除;INC (自增)、DEC (自减)。

  • 逻辑运算指令:与、或、求反、异或。

  • 移位指令

    • 算术移位:需要处理符号位;

    • 逻辑移位:不区分符号位。

  • 转移类指令:修改 PC 的值,打破程序顺序执行。

    • 无条件跳转 JUMP:分为绝对跳转(直接将目标地址送入 PC)、相对跳转(PC 加上偏移量得到目标地址)。

    • 条件转移 BRANCH:根据条件是否成立决定是否跳转。

    • CALL 调用指令:将下一条指令地址压栈保存,修改 PC 为子程序入口地址。

    • RETURN 返回指令:弹出栈内保存的返回地址,回到原程序继续执行。

    • TRAP 陷阱指令:出现意外故障,跳转到故障处理程序。

  • I/O 指令:完成 CPU 与外设之间数据、状态、控制信息的传送。

操作数的数据类型:地址、数字、字符;文本字符串,计算机内部需要转换为二进制编码,常用 ASCII、UTF‑8。

5.2 数据对齐与大小端存放方式

计算机内存按字节编址。

5.2.1 数据对齐

理论上数据可以存放在任意内存地址;为提升内存读取速度,要求数据起始地址为自身字节大小的整数倍。

结构体对齐两条规则

  1. 结构体成员对齐:每个成员起始地址是自身大小的整数倍。

  2. 结构体整体对齐:结构体总大小必须等于内部最大成员大小的整数倍。

32 位机常见类型字节大小: char:1B;short:2B;int/float:4B;double:8B;指针:4B。

定义结构体:

cpp 复制代码
struct Test{
    char a;    // 1B
    short b;   // 2B
    int c;     // 4B
    double d;  // 8B
};

设定结构体起始地址:0x00

|------------|---------|-----|-----|-----|
| 内存地址 | 字节1 | 字节2 | 字节3 | 字节4 |
| 0x00~0x03 | a(char) | 填充 | b(short) ||
| 0x04~0x07 | c(int) ||||
| 0x08~0x0B | d(double ) ||||
| 0x0C~0x0F | d(double ) ||||

总大小校验 :实际占用16B,最大成员为8B,16是8的整数倍,满足结构体整体对齐规则,最终结构体大小为 16B

核心总结:结构体对齐会产生内存填充位,牺牲少量内存空间,换取CPU高速批量读取内存的效率。

5.2.2 大小端存储

  • 大端模式 :低内存地址存放数据的高位字节,符合日常书写阅读习惯

  • 小端模式:低内存地址存放数据的低位字节。

大小端存储实例

举例:16位十六进制数据 0x1234(高位字节:0x12,低位字节:0x34),占用2字节内存,设定起始内存地址为 0x0001。

内存地址 0x0001(低地址) 0x0002(高地址)
大端模式存储内容 0x12(高位字节) 0x34(低位字节)
小端模式存储内容 0x34(低位字节) 0x12(高位字节)

5.3 指令寻址与数据寻址

5.3.1 指令寻址

PC 始终保存下一条待取指令的地址。

  1. 顺序寻址:PC 自增,程序顺序执行。

  2. 跳跃寻址:转移类指令修改 PC 的值,实现跳转分支。

5.3.2 数据寻址基础

指令地址码字段给出的一般不是真实操作数地址,称为形式地址 A ; 经过运算得到真正访问内存的地址叫做有效地址 EA

|----|-------|--------|
| OP | 寻址特征位 | 形式地址 A |

  1. 立即寻址(立即数寻址) 地址码字段存放的就是操作数本身,属于指令的一部分,只能读不能修改;不访问主存

  2. 直接寻址 形式地址 A 就是有效地址 EA;访问 1 次主存取出操作数。

  3. 寄存器寻址 地址码给出寄存器编号,有效地址就是寄存器编号;操作数存放在寄存器中,无需访存。

  4. 隐含寻址 指令中不给出操作数地址,操作数隐含在 ACC;缩短指令字长,需要额外硬件支持。

  5. 间接寻址 指令中的形式地址 A 是有效地址的内存地址。

    1. 一次间接寻址:取操作数需要2 次访存

    2. n 次间接寻址:取操作数需要 n+1 次访存(不含取指令)。

  6. 寄存器间接寻址 地址码给出寄存器编号;寄存器内存放有效地址;用有效地址访问主存得到操作数。只需要 1 次访存,相比多次间接寻址减少访存次数,扩大寻址范围。

  7. 基址寻址 EA = (BR) + A BR 基址寄存器存放程序段起始基地址,A 为指令内偏移量。也可以如下图指定一个通用寄存器R作为基址寄存器。

    1. 基址寄存器由操作系统修改,用户不可修改;面向操作系统,用于程序重定位。
  8. 变址寻址 EA = A + (IX) A 为指令中给出的基址,IX 变址寄存器存放偏移量

    1. 变址寄存器用户可以修改;面向用户,适合数组处理、循环程序。
  9. 相对寻址 EA = (PC) + A A 是偏移量;有效地址随 PC 变化,和当前指令保持固定偏移。适合程序浮动,程序加载到内存任意位置都可以正常寻址,相对跳转就使用相对寻址。

  10. 堆栈寻址 依靠堆栈指针 SP 隐含操作数地址。

  • 硬堆栈:寄存器实现;

  • 软堆栈:主存空间实现。

  1. 数组处理常用变址寻址:形式地址存放数组首地址,不断修改变址寄存器(用户即可修改),依次访问数组各个元素。

  2. 相对寻址适合程序浮动原因:有效地址由 PC 加偏移得到,只关心指令之间相对距离,和程序加载的起始物理地址无关,程序重定位后依旧可以正常寻址。

5.4 CISC 与 RISC

CISC:复杂指令集,代表 X86;RISC:精简指令集,代表 ARM、MIPS。

RISC 诞生依据:程序 80% 的语句只使用 20% 简单指令,剩余功能可以通过简单指令组合实现。

|--------|------------|---------------------|
| 对比项 | CISC | RISC |
| 指令系统 | 指令数量庞大 | 指令精简 |
| 指令字长 | 长度不固定,格式多 | 指令字长固定,格式少 |
| 寻址方式 | 寻址方式多 | 寻址方式少 |
| 访存指令 | 大量访存指令 | 仅 LOAD、STORE 可以访问内存 |
| 通用寄存器 | 数量少 | 数量多 |
| 指令频度 | 指令使用频度差距大 | 指令都是常用简单指令,频度差距小 |
| 指令执行时间 | 执行时间差异大 | 大多单时钟周期完成,流水线 |
| 控制器 | 微程序控制为主 | 硬布线控制为主 |
| 编译支持 | 难以生成高效目标代码 | 便于优化编译 |

RISC 优点

  1. 通用寄存器多,可以减少访存次数,运算速度高;

  2. 指令格式规整,便于编译优化;

  3. 指令译码简单,译码耗时短;

  4. 硬布线控制器,CPU 芯片占用面积小;

  5. 设计简单。

5.5 高级语言与机器语言对应关系

  • 低级语言:机器语言、汇编语言。

  • C 语言:编译型语言,需要经过预处理、编译、汇编、链接,生成机器语言才可运行。

  • Python:解释型语言,运行时才逐条翻译成机器代码。

GCC 编译 hello.c 完整流程

  1. 预处理:hello.c → hello.i;处理 #开头指令:文件包含、宏替换、条件编译。

  2. 编译:hello.i → hello.s;翻译为汇编文本。

  3. 汇编:hello.s → hello.o 可重定位目标文件,翻译成机器指令。

  4. 链接:hello.o 和库文件链接 → hello.exe 可执行文件。

5.6 X86 汇编基础

X86 属于 CISC 架构,来源于 8086 系列处理器,程序员可见寄存器包含:通用寄存器、指令寄存器、标志寄存器。指令格式灵活、寻址方式多,支持丰富运算与数据传送。

5.6.1 数据传送指令(重点考点)

1. MOV 数据传送指令

规则:不改变源操作数;禁止内存→内存直接传送,必须通过寄存器中转。

cpp 复制代码
# 合法示例
MOV EAX, 10        # 立即数10传入寄存器EAX
MOV EBX, EAX       # 寄存器→寄存器传送
MOV [0x1000], EAX  # 寄存器→主存

# 非法示例
MOV [0x1000], [0x2000]  # 错误!不允许内存直接传内存

2. PUSH / POP 堆栈指令

PUSH:数据压入栈;POP:数据弹出栈。

规则:PUSH 可操作寄存器、内存、立即数;POP 不能使用立即数

cpp 复制代码
PUSH EAX       # 将EAX数据压栈
PUSH 0x20      # 立即数压栈(合法)
POP EBX        # 栈顶数据弹出到EBX
POP 0x10       # 错误!POP不支持立即数

3. LEA 加载有效地址指令

功能:取出内存单元的地址 ,而非数据;光算地址,不访问内存,存粹的算术运算,所以编译器经常用 LEA 代替乘法加法,速度比普通算数指令更快。。

规则:源操作数必须是内存,目的必须是寄存器。

cpp 复制代码
LEA EAX, [EBX+4*ECX+8]  # 计算地址:EBX+4*ECX+8,存入EAX
# 无内存访问,仅做算术运算,编译器常用此优化计算

5.6.2 算术与逻辑运算指令

cpp 复制代码
INC EAX    # 自增1
DEC EBX    # 自减1
NEG EAX    # 按位取反(求补)
SHL EAX,1  # 逻辑左移1位
SHR EAX,1  # 逻辑右移1位
IMUL EBX   # 有符号乘法
IDIV ECX   # 有符号除法

5.6.3 状态与跳转指令(必考)

CMP / TEST :只修改条件码标志位,不修改操作数本身

CMP A,B 等价 SUB A,B(但不保存结果);TEST A,B 等价 AND A,B(但不保存结果)。

cpp 复制代码
; ========== CMP 用法:比大小、比相等 ==========
CMP EAX,10   ; 执行 EAX-10,设置标志位
JE label1    ; 相等跳转
JG label2    ; 大于跳转
JL label3    ; 小于跳转

; ========== TEST 用法:判二进制位、判是否为0 ==========
TEST EAX,0x1 ; 执行 EAX & 00000001,判断最低位是否为1
JNZ label4   ; 最低位为1则跳转

TEST EAX,EAX ; 执行 EAX&EAX,判断EAX是否等于0
JZ  label5   ; 结果为0(EAX=0)则跳转

无条件跳转、子程序调用

cpp 复制代码
JMP label3   # 无条件跳转

CALL func1   # 保存下一条指令地址入栈,跳转至子程序
RET          # 弹出栈中地址,返回原程序继续执行
  1. JUMP 无条件跳转:标号汇编后换算成相对偏移量(目标地址 与 下一条指令地址的差值)。

  2. JCOND 条件跳转:条件满足跳转,不满足顺序执行。

5.7 MIPS 汇编基础

MIPS 属于典型 RISC 精简指令集 ,指令规整、字长固定、仅LOAD/STORE可访存,流水线效率高。核心特性:字节编址、字对齐、默认大端存储、所有运算必须在寄存器中完成。

5.7.1 MIPS 核心硬性规则(必考)

  • 运算类指令(加减与或移位)绝对不能访问内存

  • 只有 lw(读内存)、sw(写内存) 两条指令可以访存;

  • 数据必须先 Load 进寄存器,运算后再 Store 写回内存。

bash 复制代码
# 格式:lw 目标寄存器, 偏移量(基址寄存器)
lw $t0, 0($s0)   # 将$s0指向的内存数据,加载到临时寄存器$t0
sw $t0, 4($s0)   # 将$t0的数据,写入$s0+4的内存单元

5.7.2 MIPS 与 X86 核心区别

  1. X86(CISC):指令长短不一、指令多、寻址多、任意指令可访存;

  2. MIPS(RISC):指令定长、仅Load/Store访存、运算全在寄存器、适合流水线、编译优化性强。

相关推荐
Ztt6666666661 小时前
荷口花瓶从瓶口开出一朵花
笔记·其他·百度·微信公众平台·微信开放平台
星轨初途1 小时前
LeetCode 热题 100——day7 接雨水
数据结构·c++·笔记·算法·leetcode·职场和发展
wangjialelele1 小时前
Spring Cloud 全体系学习笔记(REST、Nacos、Feign、Gateway)
java·spring boot·笔记·学习·spring·spring cloud·gateway
风曦Kisaki2 小时前
# Kubernetes(K8s)笔记Day13 :工作负载资源概述与 Job (CronJob)控制器
linux·笔记·云原生·容器·kubernetes
hongmai6668884 小时前
聊聊ESP32-C3-WROOM-02-N8这颗8MB Flash的RISC-V模组
笔记·嵌入式硬件·物联网·智能路由器·risc-v
尤乐娃子10 小时前
进入大厂(厂子大)实习Day12
前端·笔记·实习
砚凝霜12 小时前
软考网络工程师|第 6 章 密码学、哈希、数字签名、数字证书 PKI 完整备考笔记
笔记·密码学·哈希算法
QAQo7T12 小时前
Python组蓝桥杯备赛超详细知识点总结笔记_排序算法篇
笔记·python·算法·蓝桥杯·排序算法
lion_heart00112 小时前
成功日记Plus 智能记账:共享账本、预算管控、Excel 导出等一次讲清
笔记