目录

第六章:指令系统与汇编


章节概述

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

学习目标

本章重点

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

本章难点

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

6.1 机器指令

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

6.1.1 指令格式

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

指令格式示意图:

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

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

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

6.1.2 指令长度

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

定长指令

变长指令

指令长度示例:

定长指令(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 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执行        读/写内存      将结果写
地址取指令      和操作数        运算           数据           回寄存器

6.2 寻址方式

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

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    │
└─────────┴─────────┴─────────┘

特点

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)

特点

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    ; 存局部变量

特点

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 CISC与RISC

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

CISC特点

代表处理器: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特点

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

设计思想

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个)
控制方式 微程序控制 硬布线控制
流水线实现 较复杂 较简单
代码密度 较低
功耗 较高 较低
设计复杂度 硬件复杂 编译器复杂

发展趋势


6.4 汇编语言基础

6.4.1 汇编语言概述

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

汇编语言的特点

汇编语言的组成

6.4.2 汇编语言格式

典型的汇编语句格式:

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

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

语句组成

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 汇编程序设计

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

程序结构说明

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 中断与异常处理

6.6.1 中断的基本概念

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

中断的作用

中断的分类

中断处理流程:

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

6.6.2 中断向量表

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

每个中断向量占4字节:

中断向量表示意:

地址      中断号    说明
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

6.7 练习题

一、选择题

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指令)