计算机操作系统:第六章_指令系统与汇编

第六章:指令系统与汇编

本章介绍计算机指令系统的基本概念、寻址方式、CISC与RISC架构的区别,以及汇编语言程序设计基础。通过本章学习,理解计算机如何执行指令,掌握汇编语言的基本语法和程序设计方法。

学习目标

  • 理解指令的基本格式和组成部分
  • 掌握各种寻址方式的原理和应用场景
  • 理解CISC与RISC架构的特点和区别
  • 掌握x86汇编语言的基本指令和语法
  • 能够编写简单的汇编程序
  • 理解中断和异常处理机制

本章重点

  1. 指令格式和指令类型
  2. 各种寻址方式的特点和应用
  3. CISC与RISC的区别
  4. 汇编语言基本指令和伪指令
  5. 汇编程序设计方法

本章难点

  1. 复杂寻址方式的理解和应用
  2. 汇编程序设计中的寄存器管理
  3. 指令执行过程分析
  4. 中断处理机制

机器指令是计算机能直接识别和执行的二进制代码,是计算机程序的基本单位。指令系统(指令集)是计算机体系结构的核心,决定了计算机的基本功能和编程方式。

6.1.1 指令格式

机器指令是计算机能直接识别和执行的二进制代码,是计算机程序的基本单位。每条指令通常包含两部分:

  • 操作码(Opcode):指明指令要执行的操作类型,如加法、减法、数据传送、跳转等
  • 地址码(Address):指明操作数的地址或立即数,以及运算结果的存放地址
指令格式示意图:

┌─────────────────┬─────────────────────────┐
│   操作码(OP)    │      地址码(A)          │
├─────────────────┼─────────────────────────┤
│  指明操作类型   │   指明操作数位置        │
│  如:加、减、   │   如:寄存器号、        │
│  传送、跳转等   │   内存地址、立即数      │
└─────────────────┴─────────────────────────┘

典型指令格式(三地址指令):
├─────────┼─────────┼─────────┼─────────┤
│  操作码  │ 目的地址 │ 源地址1 │ 源地址2 │
│   8位   │   8位   │   8位   │   8位   │
└─────────┴─────────┴─────────┴─────────┘

根据地址码字段的数量,指令可分为:

  • 零地址指令:只有操作码,无地址码。如空操作指令NOP、停机指令HALT
  • 一地址指令:只有一个地址码。如取反、移位等单操作数指令
  • 二地址指令:两个地址码。如ADD R1, R2(R1 = R1 + R2)
  • 三地址指令:三个地址码。如ADD R1, R2, R3(R1 = R2 + R3)

6.1.2 指令长度

根据指令长度是否固定,可分为:

定长指令

  • 所有指令长度相同,通常为32位或64位
  • 译码简单,执行速度快
  • 便于流水线实现
  • 如:RISC处理器(ARM、MIPS、RISC-V)通常采用定长指令

变长指令

  • 指令长度根据操作需要变化,从1字节到15字节不等
  • 节省存储空间,代码密度高
  • 译码复杂,影响流水线效率
  • 如:x86采用变长指令(1-15字节)
指令长度示例:

定长指令(32位):
├──────────┼──────────┼──────────┼──────────┤
│  操作码   │  源寄存器 │  目的寄存器│  偏移量  │
│  8位     │   5位    │    5位   │   14位   │
└──────────┴──────────┴──────────┴──────────┘

变长指令(x86示例):
单字节指令:[操作码8位]              如:NOP (0x90)
双字节指令:[操作码8位][操作数8位]    如:PUSH imm8
三字节指令:[操作码8位][操作数16位]   如:MOV AX, imm16
四字节及以上:[前缀][操作码][ModR/M][SIB][位移][立即数]

6.1.3 指令类型

现代计算机的指令系统通常包含以下几类指令:

数据传送指令

  • MOV:数据传送,在寄存器、内存之间移动数据
  • PUSH/POP:堆栈操作,用于保存和恢复寄存器值
  • LEA:取有效地址(Load Effective Address)
  • LDS/LES:取段地址到DS/ES寄存器

算术运算指令

  • ADD/SUB:加/减法运算
  • ADC/SBB:带进位加法/带借位减法
  • MUL/IMUL:无符号/有符号乘法
  • DIV/IDIV:无符号/有符号除法
  • INC/DEC:增1/减1运算
  • NEG:求补运算(取负数)
  • CMP:比较运算(执行减法但不保存结果,只影响标志位)

逻辑运算指令

  • AND/OR/XOR:与/或/异或运算
  • NOT:非运算(取反)
  • TEST:测试运算(执行AND但不保存结果,只影响标志位)
  • SHL/SHR:逻辑左移/右移
  • SAL/SAR:算术左移/右移
  • ROL/ROR:循环左移/右移
  • RCL/RCR:带进位循环左移/右移

控制转移指令

  • JMP:无条件跳转
  • JZ/JNZ:条件跳转(为零/非零跳转)
  • JE/JNE:条件跳转(相等/不相等跳转)
  • JG/JL/JGE/JLE:有符号数比较跳转(大于/小于/大于等于/小于等于)
  • JA/JB/JAE/JBE:无符号数比较跳转(高于/低于/高于等于/低于等于)
  • CALL/RET:子程序调用/返回
  • INT/IRET:中断调用/返回
  • LOOP/LOOPE/LOOPNE:循环控制指令

输入输出指令

  • IN/OUT:端口输入/输出(x86架构)
  • 用于与外部设备进行数据交换

处理器控制指令

  • NOP:空操作
  • HLT:停机
  • CLI/STI:清除/设置中断允许标志
  • CLC/STC:清除/设置进位标志
指令类型应用示例:

; 数据传送
MOV AX, BX          ; AX = BX
MOV [SI], AX        ; 将AX存入SI指向的内存
PUSH AX             ; AX入栈
POP BX              ; 出栈到BX

; 算术运算
ADD AX, 10          ; AX = AX + 10
SUB BX, CX          ; BX = BX - CX
MUL BL              ; AX = AL × BL (无符号)
IMUL CX             ; DX:AX = AX × CX (有符号)
DIV BL              ; AL = AX ÷ BL的商, AH = 余数
INC AX              ; AX = AX + 1
DEC BX              ; BX = BX - 1
NEG AX              ; AX = -AX
CMP AX, BX          ; 比较AX和BX(设置标志位)

; 逻辑运算
AND AX, 0FFH        ; AX = AX AND 00FFH
OR BX, CX           ; BX = BX OR CX
XOR AX, AX          ; AX = 0(清零常用技巧)
NOT BX              ; BX = 按位取反
SHL AX, 1           ; AX左移1位(相当于乘以2)
SHR BX, CL          ; BX右移CL位

; 控制转移
JMP LABEL           ; 无条件跳转到LABEL
JZ ZERO_LABEL       ; 如果ZF=1,跳转
JNZ NONZERO_LABEL   ; 如果ZF=0,跳转
CALL SUBROUTINE     ; 调用子程序
RET                 ; 从子程序返回
INT 21H             ; 调用DOS中断
IRET                ; 从中断返回

6.1.4 指令执行过程

一条指令的执行通常包括以下几个阶段:

指令执行周期:

┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
│  取指   │ -> │  译码   │ -> │  执行   │ -> │  访存   │ -> │  写回   │
│   (IF)  │    │  (ID)   │    │  (EX)   │    │  (MEM)  │    │  (WB)   │
└─────────┘    └─────────┘    └─────────┘    └─────────┘    └─────────┘
     │              │              │              │              │
     ▼              ▼              ▼              ▼              ▼
从PC指向的      解析操作码      ALU执行        读/写内存      将结果写
地址取指令      和操作数        运算           数据           回寄存器
  • 取指(Fetch):根据程序计数器PC的值,从内存中读取指令
  • 译码(Decode):解析指令的操作码和地址码,确定要执行的操作
  • 执行(Execute):ALU执行算术或逻辑运算
  • 访存(Memory):如果需要,从内存读取或写入数据
  • 写回(Write Back):将运算结果写回寄存器或内存
  • 更新PC:PC指向下一条指令

寻址方式是指确定操作数地址的方法。不同的寻址方式提供了访问操作数的灵活性,是指令系统设计的重要组成部分。

6.2.1 立即寻址

操作数直接包含在指令中,作为指令的一部分。

立即寻址示例:

MOV AX, 100      ; 将立即数100送入AX寄存器
ADD BX, 50       ; BX = BX + 50
MOV CX, 0FF00H   ; CX = 0FF00H

指令执行前:AX = ?
指令执行后:AX = 100

指令格式示意:
├─────────┼─────────┼──────────┤
│ 操作码   │  寄存器  │  立即数   │
│  MOV    │   AX    │   100    │
└─────────┴─────────┴──────────┘

特点

  • 无需访问内存,执行速度最快
  • 操作数范围受指令长度限制
  • 不能用于目的操作数(不能给立即数赋值)
  • 常用于赋初值、加减常数等操作

6.2.2 直接寻址

指令中直接给出操作数的内存有效地址。

直接寻址示例:

MOV AX, [2000H]  ; 将地址2000H处的字数据送入AX
MOV [3000H], BX  ; 将BX内容存入地址3000H处
MOV AL, DATA     ; 将DATA变量(地址)处的字节送入AL

内存布局:
地址    内容
2000H   34H
2001H   12H

执行后:AX = 1234H(假设小端存储)

指令格式示意:
├─────────┼─────────┼──────────────┤
│ 操作码   │  寄存器  │  直接地址     │
│  MOV    │   AX    │   2000H     │
└─────────┴─────────┴──────────────┘

特点

  • 需要访问内存,速度较立即寻址慢
  • 地址范围受指令长度限制
  • 适用于访问固定的内存变量

6.2.3 寄存器寻址

操作数在寄存器中,指令中给出寄存器编号。

寄存器寻址示例:

MOV AX, BX       ; 将BX内容送入AX
ADD CX, DX       ; CX = CX + DX
INC AX           ; AX = AX + 1

执行前:BX = 1234H
执行后:AX = 1234H

指令格式示意:
├─────────┼─────────┼─────────┤
│ 操作码   │ 目的寄存器│ 源寄存器 │
│  MOV    │   AX    │   BX    │
└─────────┴─────────┴─────────┘

特点

  • 无需访问内存,速度快
  • 受寄存器数量限制(通常8-32个通用寄存器)
  • 是最常用的寻址方式

6.2.4 寄存器间接寻址

寄存器中存放的是操作数的内存地址,而非操作数本身。

寄存器间接寻址示例:

MOV AX, [BX]     ; 将BX指向的内存单元内容送入AX
MOV [SI], CX     ; 将CX存入SI指向的内存单元
MOV DL, [DI]     ; DL = [DI]

假设:BX = 2000H
      [2000H] = 56H
      [2001H] = 78H

执行后:AX = 7856H

指令格式示意:
├─────────┼─────────┼─────────┤
│ 操作码   │  寄存器  │ 间接寄存器│
│  MOV    │   AX    │   [BX]  │
└─────────┴─────────┴─────────┘

特点

  • 需要访问内存
  • 可以实现指针功能,访问动态地址
  • 常用于数组遍历、链表操作等

6.2.5 变址寻址

操作数地址 = 变址寄存器内容 + 位移量(偏移量)。

变址寻址示例:

MOV AX, [SI+10]       ; 地址 = SI + 10
MOV AX, ARRAY[DI]     ; 地址 = ARRAY的地址 + DI
MOV TABLE[BX], CX     ; [TABLE的地址 + BX] = CX

常用于数组访问:
SI/DI作为数组索引,位移量作为数组起始地址

数组访问示意:
数组首地址 = ARRAY (1000H)
├─────┼─────┼─────┼─────┼─────┐
│  A  │  B  │  C  │  D  │  E  │
│[1000] [1001] [1002] [1003] [1004]
└─────┴─────┴─────┴─────┴─────┘
           ▲
         SI=2
MOV AL, ARRAY[SI]  ; AL = 'C' (地址1002H)

特点

  • 适合数组、表格等结构化数据的访问
  • 通过修改变址寄存器,可以方便地遍历数组
  • x86中SI、DI、BX可作为变址寄存器

6.2.6 基址寻址

操作数地址 = 基址寄存器内容 + 位移量。

基址寻址示例:

MOV AX, [BX+20]       ; 地址 = BX + 20
MOV AX, [BP-4]        ; 地址 = BP - 4(访问栈帧中的局部变量)
MOV [BP+6], DX        ; [BP+6] = DX

常用于访问结构体或栈帧中的局部变量:
BP通常指向栈帧基址,位移量指向参数或局部变量

栈帧结构示意:
高地址 │  参数n   │ BP+2n
       │  ...    │
       │  参数1   │ BP+4
       │ 返回地址 │ BP+2
       ├─────────┤
  BP-> │ 旧BP值   │ BP
       │ 局部变量1 │ BP-2
       │ 局部变量2 │ BP-4
低地址 │  ...    │

MOV AX, [BP+4]    ; 取第一个参数
MOV [BP-2], BX    ; 存局部变量

特点

  • 常用于访问栈帧中的数据
  • x86中BX、BP可作为基址寄存器
  • BP默认使用SS段寄存器(栈段),BX默认使用DS段寄存器

6.2.7 基址变址寻址

操作数地址 = 基址寄存器 + 变址寄存器 + 位移量。

基址变址寻址示例:

MOV AX, [BX+SI+10]    ; 地址 = BX + SI + 10
MOV [BP+DI-4], CX     ; 地址 = BP + DI - 4
MOV DX, ARRAY[BX][SI] ; 地址 = ARRAY地址 + BX + SI

常用于二维数组访问:
BX = 行偏移(每行长度 × 行号)
SI = 列偏移(列号)
位移量 = 数组起始地址

二维数组布局(3行4列):
      列0   列1   列2   列3
行0  [a00] [a01] [a02] [a03]
行1  [a10] [a11] [a12] [a13]
行2  [a20] [a21] [a22] [a23]

访问a[i][j]:地址 = 基址 + i×每行字节数 + j

6.2.8 相对寻址

转移地址 = 当前PC值 + 位移量。主要用于条件跳转和循环控制。

相对寻址示例:

JMP LABEL        ; 跳转到LABEL处
JZ NEXT          ; 如果为零,跳转到NEXT
LOOP START       ; CX减1,如果不为零则跳转到START

位移量范围:
- 短跳转(Short Jump):-128到+127字节
- 近跳转(Near Jump):-32768到+32767字节
- 远跳转(Far Jump):跨段跳转

代码示意:
地址    指令
0100H   MOV AX, 0
0103H   MOV CX, 10
0106H   ADD AX, CX    <-┐
0108H   LOOP 0106H   ---┘  ; 相对位移 = 0106H - 010AH = -4
010AH   MOV SUM, AX

特点

  • 代码可重定位(位置无关代码)
  • 位移量是相对于当前指令地址的偏移
  • 节省指令长度,不需要完整的绝对地址

6.2.9 寻址方式比较

寻址方式 操作数位置 访问速度 主要用途 示例
立即寻址 指令中 最快 赋常数、加减常数 MOV AX, 100
寄存器寻址 寄存器 频繁使用的数据 MOV AX, BX
直接寻址 内存 较慢 访问固定地址变量 MOV AX, [1000H]
寄存器间接寻址 内存 较慢 指针操作 MOV AX, [BX]
变址寻址 内存 较慢 数组访问 MOV AX, [SI+10]
基址寻址 内存 较慢 结构体、栈帧访问 MOV AX, [BP+4]
基址变址寻址 内存 较慢 二维数组 MOV AX, [BX+SI]
相对寻址 内存 较慢 程序转移 JMP LABEL

6.3.1 CISC(复杂指令集计算机)

CISC特点

  • 指令系统庞大,指令数量多(通常数百条)
  • 指令长度不固定
  • 指令格式多样
  • 寻址方式复杂(多种寻址方式)
  • 指令执行周期不同(有的1周期,有的几百周期)
  • 采用微程序控制
  • 内存访问指令多样(不仅Load/Store)

代表处理器:Intel x86系列(8086、Pentium、Core i系列、Xeon)

设计思想

  • 用硬件实现复杂指令,减少软件负担
  • 减少程序代码量(代码密度高)
  • 接近高级语言的操作(如字符串操作、循环控制)
CISC指令示例(x86):

REP MOVSB        ; 重复传送字符串,一条指令完成循环
ENTER 8, 0       ; 建立栈帧,包含多条微操作
LEAVE            ; 释放栈帧
CMPSB            ; 比较字符串
SCASB            ; 扫描字符串
LOOP LABEL       ; 循环控制(CX减1,不为零则跳转)

复杂指令的微程序实现:
REP MOVSB 实际执行:
while (CX != 0) {
    [DI] = [SI];  // 传送字节
    SI++; DI++;   // 修改指针
    CX--;         // 计数器减1
}

CISC的优缺点

  • 优点:代码密度高,节省内存;编译器设计相对简单
  • 缺点:硬件复杂,功耗高;流水线设计困难;部分复杂指令使用频率低

6.3.2 RISC(精简指令集计算机)

RISC特点

  • 指令数量少(通常几十条基本指令)
  • 指令长度固定(32位或64位)
  • 指令格式规整(简化译码)
  • 寻址方式简单(通常3-5种)
  • 大多数指令单周期执行
  • 采用硬布线控制(速度快)
  • 只有Load/Store指令访问内存
  • 通用寄存器数量多(通常32个)

代表处理器:ARM、MIPS、RISC-V、SPARC、PowerPC

设计思想

  • 简化硬件设计,提高时钟频率
  • 通过编译优化提高指令级并行性
  • 80/20法则:20%的简单指令占程序执行的80%
RISC指令示例(MIPS):

# C代码:a = b + c;
# 假设a、b、c分别存放在栈偏移8、0、4处

LW  $t0, 0($sp)      # 从内存加载b到$t0 (Load Word)
LW  $t1, 4($sp)      # 从内存加载c到$t1
ADD $t2, $t0, $t1    # $t2 = $t0 + $t1
SW  $t2, 8($sp)      # 将结果存入a (Store Word)

# 特点:只有LW/SW访问内存,运算都在寄存器间进行

RISC的优缺点

  • 优点:硬件简单,功耗低;流水线效率高;易于实现超标量;编译优化空间大
  • 缺点:代码密度较低(程序体积较大);某些操作需要多条指令

6.3.3 CISC与RISC比较

特性 CISC RISC
指令数量 多(数百条) 少(几十条)
指令长度 变长 定长
指令周期 不固定(1-数百周期) 大多数单周期
寻址方式 复杂多样(10+种) 简单(3-5种)
内存访问 多种指令可访问内存 只有Load/Store
寄存器数量 较少(8-16个) 较多(32个)
控制方式 微程序控制 硬布线控制
流水线实现 较复杂 较简单
代码密度 较低
功耗 较高 较低
设计复杂度 硬件复杂 编译器复杂

发展趋势

  • 现代处理器融合两种技术(RISC内核,CISC接口)
  • x86处理器(如Intel Core系列)内部将CISC指令译码为类RISC的微操作(μops)
  • ARM处理器增加部分复杂指令提高代码密度(Thumb指令集)
  • 两者界限逐渐模糊,取长补短

6.4.1 汇编语言概述

汇编语言是机器语言的符号化表示,使用助记符代替二进制操作码,使用标号和符号代替地址。

汇编语言的特点

  • 与机器语言一一对应,是低级语言
  • 直接控制硬件,效率高
  • 可移植性差(与特定处理器绑定)
  • 编程效率低,但执行效率高

汇编语言的组成

  • 指令语句:可执行语句,产生机器码(如MOV、ADD、JMP)
  • 伪指令语句:指导汇编器工作,不产生机器码(如DB、DW、SEGMENT)
  • 宏指令:用户自定义的指令序列,用于代码复用

6.4.2 汇编语言格式

典型的汇编语句格式:

[标号:]  操作码  [操作数]  [;注释]

示例:
START:  MOV  AX, DATA    ; 初始化数据段寄存器
        ADD  AX, BX      ; AX = AX + BX
        JMP  LOOP1       ; 跳转到LOOP1
        
; 标号START表示该指令的地址,可用于跳转
; 操作码MOV、ADD、JMP指明操作类型
; 操作数指定参与运算的数据
; 分号后面是注释,不影响程序执行

语句组成

  • 标号:可选,代表指令的符号地址,用于跳转和调用
  • 操作码:必需的,指定操作类型(指令助记符或伪指令)
  • 操作数:指令需要的操作数,可以是0-3个
  • 注释:可选,以分号开头,用于说明代码功能

6.4.3 常用伪指令

段定义伪指令

SEGMENT/ENDS    定义段的开始和结束
ASSUME          设定段寄存器与段的对应关系

示例:
DATA SEGMENT        ; 数据段开始
    VAR1 DB  10     ; 定义变量
DATA ENDS           ; 数据段结束

CODE SEGMENT        ; 代码段开始
    ASSUME CS:CODE, DS:DATA  ; 设定CS指向CODE段,DS指向DATA段
    ...
CODE ENDS           ; 代码段结束

数据定义伪指令

DB      定义字节(8位)
DW      定义字(16位)
DD      定义双字(32位)
DQ      定义四字(64位)
DT      定义十字节(80位,用于BCD码)

示例:
DATA SEGMENT
    VAR1 DB  10             ; 定义字节变量,初值为10
    VAR2 DW  1000H          ; 定义字变量,初值为1000H
    VAR3 DD  12345678H      ; 定义双字变量
    VAR4 DB  'HELLO'        ; 定义字符串,每个字符占一个字节
    ARRAY DB  10 DUP(0)     ; 定义10个字节的数组,初值都为0
    TABLE DW  5 DUP(1, 2)   ; 定义10个字的数组:1,2,1,2,1,2,1,2,1,2
    BUF  DB  100 DUP(?)     ; 定义100字节的缓冲区,未初始化
DATA ENDS

其他伪指令

ORG     设定程序起始地址
END     程序结束标记,可指定入口点
EQU     常量定义(等价)
PROC/ENDP   过程(子程序)定义
PUBLIC/EXTRN    声明公共符号/外部符号

示例:
ORG 100H            ; 从地址100H开始存放代码
MAX EQU 100         ; 定义常量MAX=100
PI  EQU 3.14159     ; 定义常量PI

MAIN PROC NEAR      ; 定义过程MAIN,NEAR表示近过程
    ...
    RET
MAIN ENDP

END MAIN            ; 程序结束,入口点为MAIN

6.4.4 x86常用指令详解

数据传送指令

MOV     传送数据(寄存器、内存之间)
MOVSX   带符号扩展传送
MOVZX   带零扩展传送
XCHG    交换两个操作数的值
PUSH    将数据压入堆栈
POP     从堆栈弹出数据
LEA     取有效地址(Load Effective Address)
LDS/LES 取段地址到DS/ES寄存器
PUSHF/POPF  标志寄存器入栈/出栈

示例:
MOV AX, BX          ; AX = BX
MOV AL, [SI]        ; AL = [SI]
MOV [DI], DX        ; [DI] = DX
MOVSX EAX, AX       ; EAX = 符号扩展的AX(16位扩展到32位)
MOVZX EBX, BL       ; EBX = 零扩展的BL(8位扩展到32位)
XCHG AX, BX         ; AX和BX交换
PUSH AX             ; SP = SP - 2, [SP] = AX
POP BX              ; BX = [SP], SP = SP + 2
LEA SI, ARRAY       ; SI = ARRAY的地址(不是内容)

算术运算指令

ADD     加法
ADC     带进位加法(用于多字节加法)
SUB     减法
SBB     带借位减法(用于多字节减法)
MUL     无符号乘法
IMUL    有符号乘法
DIV     无符号除法
IDIV    有符号除法
INC     加1
DEC     减1
NEG     求负(取补码)
CMP     比较(执行减法但不保存结果,只影响标志位)

示例:
ADD AX, BX          ; AX = AX + BX
ADC DX, 0           ; DX = DX + 0 + CF(带进位加法)
SUB AX, 10          ; AX = AX - 10
SBB DX, 0           ; DX = DX - 0 - CF(带借位减法)
MUL BL              ; AX = AL × BL(无符号乘法)
IMUL CX             ; DX:AX = AX × CX(有符号乘法,16位×16位=32位)
DIV BL              ; AL = AX ÷ BL的商, AH = 余数(无符号除法)
IDIV BX             ; AX = DX:AX ÷ BX的商, DX = 余数(有符号除法)
INC AX              ; AX = AX + 1(不影响CF)
DEC BX              ; BX = BX - 1(不影响CF)
NEG AX              ; AX = -AX(按位取反后加1)
CMP AX, BX          ; 比较AX和BX(AX - BX,只影响标志位)

逻辑运算指令

AND     逻辑与
OR      逻辑或
XOR     逻辑异或
NOT     逻辑非(取反)
TEST    测试(执行AND但不保存结果,只影响标志位)
SHL/SAL 逻辑/算术左移(低位补0)
SHR     逻辑右移(高位补0)
SAR     算术右移(高位补符号位)
ROL     循环左移
ROR     循环右移
RCL     带进位循环左移
RCR     带进位循环右移

示例:
AND AX, 0FFH        ; AX = AX AND 00FFH(屏蔽高8位)
OR BL, 80H          ; BL = BL OR 80H(设置最高位为1)
XOR AX, AX          ; AX = 0(清零,比MOV AX, 0更快)
NOT BX              ; BX = 按位取反
TEST AL, 80H        ; 测试AL的最高位是否为1(设置ZF)
SHL AX, 1           ; AX左移1位,相当于AX × 2
SHR BX, CL          ; BX右移CL位
SAR AX, 2           ; AX算术右移2位(保持符号位)
ROL AX, 1           ; AX循环左移1位(最高位移到最低位和CF)
RCL AX, 1           ; AX带进位循环左移1位

控制转移指令

JMP     无条件跳转
JE/JZ   等于/为零跳转(ZF=1)
JNE/JNZ 不等于/非零跳转(ZF=0)
JG/JNLE 大于跳转(有符号,ZF=0且SF=OF)
JGE/JNL 大于等于跳转(有符号,SF=OF)
JL/JNGE 小于跳转(有符号,SF≠OF)
JLE/JNG 小于等于跳转(有符号,ZF=1或SF≠OF)
JA/JNBE 高于跳转(无符号,CF=0且ZF=0)
JAE/JNB 高于等于跳转(无符号,CF=0)
JB/JNAE 低于跳转(无符号,CF=1)
JBE/JNA 低于等于跳转(无符号,CF=1或ZF=1)
JC/JNC  有/无进位跳转
JO/JNO  溢出/无溢出跳转
JS/JNS  为负/为正跳转
CALL    调用子程序
RET     从子程序返回
INT     中断调用
IRET    从中断返回
LOOP    循环(CX减1,不为零则跳转)
LOOPE/LOOPZ  等于/为零循环
LOOPNE/LOOPNZ 不等于/非零循环

示例:
JMP LABEL           ; 无条件跳转到LABEL
JZ ZERO_LABEL       ; 如果ZF=1(结果为0),跳转
JNZ NONZERO_LABEL   ; 如果ZF=0(结果非0),跳转
JG GREATER_LABEL    ; 有符号数比较,大于则跳转
JA ABOVE_LABEL      ; 无符号数比较,高于则跳转
CALL SUBROUTINE     ; 调用子程序(PUSH IP, JMP)
RET                 ; 从子程序返回(POP IP)
INT 21H             ; 调用DOS中断21H
IRET                ; 从中断返回(恢复FLAGS、CS、IP)

6.5.1 程序基本结构

一个完整的汇编程序通常包括数据段、代码段和堆栈段的定义。

; 完整汇编程序示例:显示"Hello World!"
DATA SEGMENT
    MSG DB 'Hello World!', '$'    ; 定义字符串,$表示结束符
    COUNT DW 10                   ; 定义字变量
DATA ENDS

CODE SEGMENT
    ASSUME CS:CODE, DS:DATA       ; 设定段寄存器

START:
    MOV AX, DATA
    MOV DS, AX                    ; 初始化数据段寄存器
    
    LEA DX, MSG                   ; DX指向字符串
    MOV AH, 09H                   ; DOS功能号:显示字符串
    INT 21H                       ; 调用DOS中断
    
    MOV AH, 4CH                   ; DOS功能号:返回DOS
    INT 21H                       ; 调用DOS中断

CODE ENDS
END START                         ; 程序结束,入口点为START

程序结构说明

  • 数据段(DATA SEGMENT):存放变量、常量、字符串等数据
  • 代码段(CODE SEGMENT):存放程序指令
  • 堆栈段(STACK SEGMENT):存放临时数据和返回地址
  • 初始化:程序开始时需要初始化DS寄存器
  • 中断调用:使用INT指令调用DOS功能

6.5.2 分支程序设计

分支程序通过条件判断实现不同的执行路径。

; 比较两个数的大小,将大数存入MAX
DATA SEGMENT
    NUM1 DW 50
    NUM2 DW 30
    MAX  DW ?
DATA ENDS

CODE SEGMENT
    ASSUME CS:CODE, DS:DATA

START:
    MOV AX, DATA
    MOV DS, AX
    
    MOV AX, NUM1
    CMP AX, NUM2                  ; 比较NUM1和NUM2
    JGE LARGER                    ; 如果NUM1 >= NUM2,跳转到LARGER
    MOV AX, NUM2                  ; 否则,AX = NUM2
LARGER:
    MOV MAX, AX                   ; MAX = 较大数
    
    MOV AH, 4CH
    INT 21H

CODE ENDS
END START

多分支结构

; 根据AL的值(0-2)跳转到不同处理分支
    CMP AL, 0
    JE  CASE_0
    CMP AL, 1
    JE  CASE_1
    CMP AL, 2
    JE  CASE_2
    JMP DEFAULT_CASE

CASE_0:
    ; 处理情况0
    JMP END_SWITCH
CASE_1:
    ; 处理情况1
    JMP END_SWITCH
CASE_2:
    ; 处理情况2
    JMP END_SWITCH
DEFAULT_CASE:
    ; 默认处理
END_SWITCH:

6.5.3 循环程序设计

循环程序用于重复执行一段代码。

; 计算1到100的累加和
DATA SEGMENT
    SUM DW ?
DATA ENDS

CODE SEGMENT
    ASSUME CS:CODE, DS:DATA

START:
    MOV AX, DATA
    MOV DS, AX
    
    MOV CX, 100                   ; 循环计数器,从100到1
    MOV AX, 0                     ; 累加器清零
    
LOOP1:
    ADD AX, CX                    ; AX = AX + CX
    DEC CX                        ; 计数器减1
    JNZ LOOP1                     ; 如果不为零,继续循环
    
    MOV SUM, AX                   ; 保存结果(结果为5050)
    
    MOV AH, 4CH
    INT 21H

CODE ENDS
END START

LOOP指令循环

; 使用LOOP指令实现循环
    MOV CX, 10          ; 循环10次
NEXT:
    ; 循环体代码
    LOOP NEXT           ; CX减1,如果不为零则跳转到NEXT

; 带条件的循环(LOOPE/LOOPNE)
    MOV CX, 100
    MOV SI, 0
SEARCH:
    CMP BUF[SI], AL     ; 比较
    LOOPE SEARCH        ; 相等且CX≠0时继续循环

嵌套循环

; 嵌套循环示例:延时程序
    MOV CX, 1000        ; 外层循环次数
OUTER_LOOP:
    PUSH CX             ; 保存外层计数器
    MOV CX, 1000        ; 内层循环次数
INNER_LOOP:
    NOP                 ; 空操作
    LOOP INNER_LOOP
    POP CX              ; 恢复外层计数器
    LOOP OUTER_LOOP

6.5.4 子程序设计

子程序(过程/函数)用于封装可复用的代码块。

; 子程序示例:计算阶乘
DATA SEGMENT
    N    DW 5
    RESULT DW ?
DATA ENDS

CODE SEGMENT
    ASSUME CS:CODE, DS:DATA

; 阶乘子程序
; 入口:AX = n
; 出口:AX = n!
; 使用递归实现
FACT PROC NEAR
    CMP AX, 1
    JLE BASE_CASE                 ; 如果n <= 1,返回1
    
    PUSH AX                       ; 保存n
    DEC AX                        ; AX = n-1
    CALL FACT                     ; 递归调用
    POP BX                        ; 恢复n到BX
    MUL BX                        ; AX = AX * BX = (n-1)! * n
    JMP DONE
    
BASE_CASE:
    MOV AX, 1
DONE:
    RET
FACT ENDP

START:
    MOV AX, DATA
    MOV DS, AX
    
    MOV AX, N
    CALL FACT
    MOV RESULT, AX                ; 保存5! = 120
    
    MOV AH, 4CH
    INT 21H

CODE ENDS
END START

参数传递方法

; 方法1:寄存器传递参数
; 入口:AX = 参数1, BX = 参数2
; 出口:AX = 返回值
ADD_PROC PROC
    ADD AX, BX
    RET
ADD_PROC ENDP

; 方法2:堆栈传递参数
; 调用前:PUSH 参数2, PUSH 参数1
ADD_PROC2 PROC
    PUSH BP
    MOV BP, SP
    MOV AX, [BP+4]      ; 取参数1
    MOV BX, [BP+6]      ; 取参数2
    ADD AX, BX
    POP BP
    RET 4               ; 返回并清理4字节参数
ADD_PROC2 ENDP

; 调用示例:
    PUSH 30             ; 参数2
    PUSH 20             ; 参数1
    CALL ADD_PROC2      ; AX = 50

6.5.5 数组和字符串操作

; 数组操作示例:查找最大值
DATA SEGMENT
    ARRAY DW 23, 56, 12, 89, 34, 78, 45, 67
    COUNT EQU 8
    MAX   DW ?
DATA ENDS

CODE SEGMENT
    ASSUME CS:CODE, DS:DATA

START:
    MOV AX, DATA
    MOV DS, AX
    
    LEA SI, ARRAY         ; SI指向数组
    MOV CX, COUNT         ; 元素个数
    MOV AX, [SI]          ; 假设第一个元素最大
    DEC CX                ; 剩余元素数
    ADD SI, 2             ; 指向下一个元素
    
FIND_MAX:
    CMP AX, [SI]          ; 比较当前最大值与下一个元素
    JGE NEXT_ELEM         ; 如果AX >= [SI],跳过
    MOV AX, [SI]          ; 更新最大值
NEXT_ELEM:
    ADD SI, 2             ; 指向下一个元素
    LOOP FIND_MAX
    
    MOV MAX, AX           ; 保存最大值
    
    MOV AH, 4CH
    INT 21H

CODE ENDS
END START

字符串操作指令

; x86字符串操作指令(配合重复前缀使用)
MOVSB   ; 传送字节:[DI] = [SI], SI±1, DI±1
MOVSW   ; 传送字:[DI] = [SI], SI±2, DI±2
CMPSB   ; 比较字节:[SI] - [DI]
SCASB   ; 扫描字节:AL - [DI]
LODSB   ; 加载字节:AL = [SI]
STOSB   ; 存储字节:[DI] = AL

; 重复前缀
REP     ; 重复CX次
REPE/REPZ   ; 相等/为零时重复(CX≠0且ZF=1)
REPNE/REPNZ ; 不相等/非零时重复(CX≠0且ZF=0)

; 示例:复制字符串
    LEA SI, SOURCE
    LEA DI, DEST
    MOV CX, 100
    CLD                   ; 清除方向标志(DF=0,地址递增)
    REP MOVSB             ; 复制100字节

; 示例:查找字符
    LEA DI, STRING
    MOV AL, '$'           ; 查找结束符
    MOV CX, 100
    CLD
    REPNE SCASB           ; 查找字符

6.6.1 中断的基本概念

中断是指在程序执行过程中,由于某种事件发生而暂停当前程序的执行,转去执行相应的中断处理程序,处理完毕后再返回原程序继续执行。

中断的作用

  • 实现CPU与I/O设备的并行工作
  • 处理突发事件(如电源故障)
  • 实现多任务调度
  • 提供系统调用接口

中断的分类

  • 硬件中断:由外部设备产生
    1. 可屏蔽中断(INTR):可通过IF标志屏蔽
    2. 非屏蔽中断(NMI):用于紧急情况,不可屏蔽
  • 软件中断:由INT指令产生,用于系统调用
  • 异常:CPU执行过程中产生的错误(如除零、页错误)
中断处理流程:

正常程序执行:  A -> B -> C -> D -> E
                       ↓
                    中断发生(如按键)
                       ↓
                    保存现场(FLAGS、CS、IP)
                       ↓
                    执行中断处理程序
                       ↓
                    恢复现场
                       ↓
                    返回:-> D -> E

6.6.2 中断向量表

中断向量表是存放中断处理程序入口地址的表格。在x86实模式下,位于内存最低1KB(地址00000H-003FFH)。

每个中断向量占4字节:

  • 低2字节:IP(偏移地址)
  • 高2字节:CS(段地址)
中断向量表示意:

地址      中断号    说明
00000H    0         除零错误
00004H    1         单步中断
00008H    2         非屏蔽中断
0000CH    3         断点中断
00010H    4         溢出中断
00014H    5         打印屏幕
00080H    20H       DOS中断
00084H    21H       DOS系统调用(最常用)

中断向量表结构(每个表项4字节):
├─────────┼─────────┤
│  IP偏移  │  CS段址  │
│  16位   │  16位   │
└─────────┴─────────┘

常用DOS中断(INT 21H)

功能号(AH) 功能 入口参数 出口参数
01H 键盘输入并回显 AL = 输入字符
02H 显示输出 DL = 输出字符
09H 显示字符串 DS:DX = 字符串地址($结尾)
0AH 缓冲输入 DS:DX = 缓冲区地址 缓冲区填充
4CH 返回DOS AL = 返回码

6.6.3 中断处理程序设计

; 自定义中断处理程序示例
CODE SEGMENT
    ASSUME CS:CODE, DS:CODE

; 中断处理程序:显示一个字符'A'
MY_INT PROC FAR
    PUSH AX                   ; 保存寄存器
    PUSH DX
    
    MOV DL, 'A'
    MOV AH, 02H
    INT 21H                   ; 显示字符
    
    POP DX                    ; 恢复寄存器
    POP AX
    IRET                      ; 中断返回
MY_INT ENDP

; 设置中断向量
    MOV AX, 0
    MOV ES, AX                ; ES = 0(中断向量表段)
    MOV BX, 4 * 60H           ; 中断号60H的向量地址
    
    MOV ES:[BX], OFFSET MY_INT    ; 设置IP
    MOV ES:[BX+2], CS             ; 设置CS

; 调用自定义中断
    INT 60H

CODE ENDS
END

1. 一条指令通常由(  )和地址码两部分组成。
   A. 操作数    B. 操作码    C. 指令码    D. 地址
2. 在立即寻址方式中,操作数存放在(  )
   A. 寄存器    B. 内存单元    C. 指令中    D. 栈中
3. RISC的特点不包括(  )
   A. 指令数量少    B. 指令长度固定    
   C. 采用微程序控制    D. 只有Load/Store访问内存
4. 下列指令中,属于数据传送指令的是(  )
   A. ADD    B. MOV    C. JMP    D. CMP
5. 汇编语言中,用于定义字数据的伪指令是(  )
   A. DB    B. DW    C. DD    D. DQ
6. 在基址寻址中,有效地址等于(  )
   A. 基址寄存器内容    B. 基址寄存器内容 + 位移量
   C. 变址寄存器内容    D. 指令中的地址
7. 执行CALL指令时,CPU首先将(  )压入堆栈
   A. 段地址    B. 偏移地址    C. 标志寄存器    D. 下一条指令地址
8. 下列哪种寻址方式执行速度最快(  )
   A. 立即寻址    B. 直接寻址    C. 寄存器寻址    D. 寄存器间接寻址
9. x86处理器采用(  )指令集架构
   A. CISC    B. RISC    C. VLIW    D. EPIC
10. 中断向量表在实模式下位于内存的(  )
   A. 高端地址    B. 低端地址(00000H-003FFH)
   C. 中间地址    D. 由程序指定
1. 寻址方式是指确定_______地址的方法。
2. CISC是_______的缩写,RISC是_______的缩写。
3. 在x86汇编中,MOV AX, [BX]采用的是_______寻址方式。
4. 汇编语言的语句由_______、_______、_______和注释组成。
5. 伪指令_______用于定义字节数据,_______用于定义字数据。
6. 指令执行周期通常包括取指、_______、_______、访存和写回五个阶段。
7. x86处理器有_______个通用寄存器(16位模式下)。
8. LOOP指令使用_______寄存器作为计数器。
9. 在堆栈操作中,PUSH指令使SP_______(增加/减少),POP指令使SP_______(增加/减少)。
10. IRET指令用于从_______返回。
1. 简述立即寻址、直接寻址和寄存器寻址的区别。
2. 比较CISC和RISC的主要特点。
3. 什么是汇编语言?它与机器语言和高级语言有什么区别?
4. 简述子程序调用和返回的过程。
5. 什么是中断?中断处理的基本过程是什么?
6. 简述MESI协议中的四种状态。
1. 编写汇编程序,计算数组中10个元素的累加和,并求平均值。
2. 编写汇编程序,实现两个16位无符号数的乘法,结果存入32位变量。
3. 编写汇编程序,从键盘输入一个字符串,统计其中字母的个数。
4. 编写子程序,实现两个双字(32位)数的加法。

参考答案

一、选择题:1.B 2.C 3.C 4.B 5.B 6.B 7.D 8.C 9.A 10.B

二、填空题:

1. 操作数
2. 复杂指令集计算机、精简指令集计算机
3. 寄存器间接
4. 标号、操作码、操作数
5. DB、DW
6. 译码、执行
7. 8(AX, BX, CX, DX, SP, BP, SI, DI)
8. CX
9. 减少、增加
10. 中断

三、简答题:

1. 立即寻址的操作数在指令中,执行最快但只能用于源操作数;直接寻址的操作数在内存中,指令中包含操作数的内存地址;寄存器寻址的操作数在寄存器中,执行速度快,是最常用的寻址方式。
2. CISC指令数量多、长度可变、寻址方式复杂、采用微程序控制;RISC指令数量少、长度固定、寻址方式简单、采用硬布线控制、只有Load/Store访问内存。
3. 汇编语言是机器语言的符号化表示,用助记符代替二进制操作码。与机器语言相比更易读写,与高级语言相比更接近硬件,执行效率高但可移植性差。
4. CALL指令将返回地址压入堆栈,然后跳转到子程序;子程序执行RET指令时,从堆栈弹出返回地址并跳转回去。
5. 中断是程序执行过程中因某种事件暂停当前程序,转去执行中断处理程序,完成后返回原程序。过程包括:中断请求、中断响应、保存现场、执行处理程序、恢复现场、中断返回。
6. M(Modified):已修改,独占;E(Exclusive):独占,未修改;S(Shared):共享;I(Invalid):无效。

四、程序设计题:

1. 略(参考6.5.3循环程序示例,增加除法求平均)
2. 使用MUL指令,16位×16位=32位结果,高16位在DX,低16位在AX
3. 使用INT 21H功能0AH输入字符串,遍历判断每个字符是否在'A'-'Z'或'a'-'z'范围内
4. 先加低16位,再带进位加高16位(使用ADC指令)

该主题尚不存在

您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。

  • 计算机操作系统/第六章_指令系统与汇编.txt
  • 最后更改: 2026/02/09 23:29
  • 张叶安