第七章 汇编语言基础

汇编语言(Assembly Language)是一种面向机器的低级程序设计语言,它使用助记符来表示机器指令,使用符号地址代替机器地址,是机器语言(二进制代码)的符号化表示。

汇编语言与机器语言之间存在一一对应的关系,汇编语言程序需要通过汇编程序(Assembler)翻译成机器语言程序才能在计算机上执行。这种翻译过程称为汇编。

汇编语言的主要特点

优点: - 执行效率高:汇编语言程序经汇编后生成机器码,执行速度与机器语言相同 - 占用存储空间小:程序员可以直接控制存储空间的使用 - 可以直接访问硬件资源:便于编写设备驱动程序和嵌入式程序 - 精确控制程序执行:适合对时间和空间要求严格的场合

缺点: - 可移植性差:不同CPU架构的汇编语言不同 - 编程效率低:编写和调试困难,开发周期长 - 可读性差:程序难以理解维护 - 容易出错:需要程序员管理大量细节

尽管高级语言已经成为主流,汇编语言在以下领域仍然不可替代:

系统软件开发: - 操作系统内核:启动代码、中断处理、进程调度 - 编译器:代码生成、运行时库 - 设备驱动程序:直接与硬件交互

嵌入式系统: - 对代码体积和执行速度要求严格的嵌入式应用 - 启动引导程序(Bootloader) - 实时控制系统

性能关键代码: - 加密算法核心 - 数字信号处理 - 视频编解码 - 高性能计算库

逆向工程与安全: - 病毒分析 - 漏洞挖掘 - 软件破解与保护

一个完整的汇编语言源程序通常由以下几个部分组成:

指令语句:是可执行语句,汇编后生成对应的机器码,在程序运行时执行。 格式:[标号:] 操作码 [操作数] [;注释]

伪指令语句:是不可执行语句,用于指示汇编程序如何进行汇编,不生成机器码。 格式:[名字] 伪操作 [参数] [;注释]

宏指令:是用户自定义的指令序列,汇编时会被展开成多条指令。

注释:以分号“;”开头,用于说明程序的功能,增强可读性。

标识符:是程序员定义的符号名称,用于表示常量、变量、标号、段名等。 - 由字母、数字和下划线组成 - 不能以数字开头 - 不能使用汇编语言的保留字 - 不区分大小写(某些汇编器区分)

常量:是固定不变的数值,可以是: - 数值常量:二进制(后缀B)、八进制(后缀Q或O)、十进制(默认或后缀D)、十六进制(后缀H) - 字符常量:用单引号括起来的ASCII字符 - 字符串常量:用引号括起来的字符序列

变量:是存储器中数据的符号地址,具有类型属性(字节、字、双字等)。

标号:是指令的符号地址,代表该指令在存储器中的位置。标号后面通常跟冒号。

表达式:由常量、变量、标号和运算符组成的式子,汇编时计算其值。

典型的汇编语句格式如下:

``` [标号] 操作码/伪操作 [操作数] [;注释] ```

标号字段:可选,用于标识该语句的地址。标号可以被其他指令引用。

操作码字段:必需,指明指令或伪操作的操作类型。

操作数字段:根据指令的要求,可以有零个、一个或两个操作数。多个操作数之间用逗号分隔。

注释字段:以分号开始,直到行尾。注释对程序执行没有影响,仅用于说明。

数据定义伪指令用于为数据分配存储空间并初始化。

DB(Define Byte):定义字节数据,每个数据占1字节。 ``` DATA1 DB 10H ;定义一个字节,值为10H DATA2 DB 'ABCD' ;定义4个字节的字符串 ```

DW(Define Word):定义字数据(2字节),低位字节在前。 ``` DATA3 DW 1234H ;定义一个字,值为1234H ```

DD(Define Doubleword):定义双字数据(4字节)。 ``` DATA4 DD 12345678H ;定义一个双字 ```

DQ(Define Quadword):定义四字数据(8字节)。

DT(Define Ten Bytes):定义十字节数据(10字节),常用于BCD码。

DUP操作符:用于重复定义相同的数据。 ``` BUFFER DB 100 DUP(0) ;定义100个字节的缓冲区,初始化为0 TABLE DW 10 DUP(?) ;定义10个字的表,不初始化 ```

EQU(Equate):为符号定义一个常量值,该值在程序中不能改变。 ``` COUNT EQU 100 ;定义符号COUNT等于100 PORT EQU 3F8H ;定义符号PORT等于3F8H ```

=(等号):与EQU类似,但允许重新定义。 ``` VALUE = 10 VALUE = 20 ;允许重新定义 ```

LABEL:为当前位置定义一个具有指定类型的标号。 ``` BUFFER LABEL WORD ;BUFFER为字类型 BUFFER DB 100 DUP(0);实际分配100个字节 ```

段定义伪指令用于定义程序的逻辑段。

SEGMENT/ENDS:定义段的开始和结束。 ``` CODE SEGMENT ;代码段开始

  ...

CODE ENDS ;代码段结束 ```

ASSUME:告诉汇编程序各段寄存器与逻辑段的对应关系。 ``` ASSUME CS:CODE, DS:DATA, SS:STACK ```

注意:ASSUME只是说明性的,实际的段寄存器赋值需要用MOV指令完成。

GROUP:将多个段组合成一个段组,使它们共享同一个段地址。

过程(子程序)的定义使用PROC和ENDP伪指令。

``` 过程名 PROC [NEAR/FAR]

  ;过程体
  RET

过程名 ENDP ```

NEAR:近过程,与调用程序在同一代码段内,返回时只恢复IP。

FAR:远过程,与调用程序在不同代码段,返回时恢复CS和IP。

ORG(Origin):指定后续代码或数据的起始偏移地址。 ``` ORG 100H ;从偏移100H开始存放数据或代码 ```

END:表示源程序结束。 ``` END [标号] ;可选标号指明程序入口点 ```

PUBLIC:说明本模块定义的符号可供其他模块使用。

EXTRN:说明本模块使用的符号在其他模块中定义。

INCLUDE:将指定的文件插入到当前位置。

MOV指令:最基本的传送指令,格式为MOV 目的, 源。 规则: - 两个操作数不能同时为存储器操作数 - 两个操作数不能同时为段寄存器 - 目的操作数不能为立即数 - 不能向CS传送数据 - 立即数不能直接传送给段寄存器

PUSH/POP指令:堆栈操作指令。 - PUSH 源:将字或双字压入堆栈,SP=SP-2(或4) - POP 目的:从堆栈弹出字或双字,SP=SP+2(或4) 规则:操作数不能是立即数。

XCHG指令:交换指令,交换两个操作数的值。 规则:不能交换两个存储器操作数,操作数不能是立即数。

LEA指令:装入有效地址。 ``` LEA BX, BUFFER ;将BUFFER的偏移地址送入BX ```

LDS/LES指令:装入段地址和偏移地址。 ``` LDS SI, [BX] ;将BX所指的双字装入DS:SI ```

XLAT指令:查表转换指令。 ``` XLAT ;AL ← [BX+AL] ``` 执行前:BX=表首地址,AL=索引值 执行后:AL=查表所得值

加法指令: - ADD 目的, 源:目的 = 目的 + 源 - ADC 目的, 源:目的 = 目的 + 源 + CF(带进位加) - INC 目的:目的 = 目的 + 1

减法指令: - SUB 目的, 源:目的 = 目的 - 源 - SBB 目的, 源:目的 = 目的 - 源 - CF(带借位减) - DEC 目的:目的 = 目的 - 1 - NEG 目的:目的 = 0 - 目的(取负) - CMP 目的, 源:目的 - 源,结果不保存,只影响标志位

乘法指令: - MUL 源(无符号乘法)

  1. 字节乘法:AX = AL × 源
  2. 字乘法:DX:AX = AX × 源

- IMUL 源(带符号乘法)

除法指令: - DIV 源(无符号除法)

  1. 字节除法:AL = AX ÷ 源的商,AH = 余数
  2. 字除法:AX = DX:AX ÷ 源的商,DX = 余数

- IDIV 源(带符号除法)

【例题】编写程序计算:(A + B) × C - D,其中A、B、C、D都是字节变量。

``` DATA SEGMENT

  A DB 10
  B DB 20
  C DB 5
  D DB 15
  RESULT DW ?

DATA ENDS

CODE SEGMENT

  ASSUME CS:CODE, DS:DATA

START:

  MOV AX, DATA
  MOV DS, AX
  
  MOV AL, A       ;AL = A
  ADD AL, B       ;AL = A + B
  MUL C           ;AX = (A + B) × C
  SUB AX, D       ;AX = (A + B) × C - D
  MOV RESULT, AX  ;保存结果
  
  MOV AH, 4CH
  INT 21H

CODE ENDS END START ```

逻辑运算指令: - AND 目的, 源:按位与,用于清零某些位 - OR 目的, 源:按位或,用于置位某些位 - XOR 目的, 源:按位异或,用于取反某些位或清零寄存器 - NOT 目的:按位取反 - TEST 目的, 源:测试,执行AND但不保存结果

移位指令: - SHL 目的, 计数值:逻辑左移,低位补0,高位进CF - SHR 目的, 计数值:逻辑右移,高位补0,低位进CF - SAL 目的, 计数值:算术左移(同SHL) - SAR 目的, 计数值:算术右移,高位补符号位

循环移位指令: - ROL 目的, 计数值:循环左移,高位到低位和CF - ROR 目的, 计数值:循环右移,低位到高位和CF - RCL 目的, 计数值:带进位循环左移 - RCR 目的, 计数值:带进位循环右移

【例题】将AL中的高4位与低4位互换。 ``` MOV CL, 4 ROL AL, CL ;循环左移4位 ```

【例题】将AX中的内容乘以10(不使用乘法指令)。 ``` SHL AX, 1 ;AX = AX × 2 MOV BX, AX ;BX = AX × 2 SHL AX, 1 ;AX = AX × 4 SHL AX, 1 ;AX = AX × 8 ADD AX, BX ;AX = AX × 8 + AX × 2 = AX × 10 ```

无条件转移指令: - JMP 目标:无条件跳转到目标地址

  1. 短转移:JMP SHORT 标号(-128~+127字节)
  2. 近转移:JMP NEAR PTR 标号(同一代码段)
  3. 远转移:JMP FAR PTR 标号(不同代码段)

条件转移指令:所有条件转移都是短转移(在386及以上处理器中可以是近转移)。

根据单个标志位判断: - JC/JNC:CF=1/0时转移 - JE/JZ:ZF=1时转移 - JNE/JNZ:ZF=0时转移 - JS/JNS:SF=1/0时转移 - JO/JNO:OF=1/0时转移 - JP/JNP:PF=1/0时转移

根据两个标志位判断(无符号数比较): - JA/JNBE:高于转移(CF=0且ZF=0) - JAE/JNB:高于等于转移(CF=0) - JB/JNAE:低于转移(CF=1) - JBE/JNA:低于等于转移(CF=1或ZF=1)

根据两个标志位判断(有符号数比较): - JG/JNLE:大于转移(ZF=0且SF=OF) - JGE/JNL:大于等于转移(SF=OF) - JL/JNGE:小于转移(SF≠OF) - JLE/JNG:小于等于转移(ZF=1或SF≠OF)

循环控制指令: - LOOP 标号:CX=CX-1,若CX≠0则转移 - LOOPE/LOOPZ 标号:CX=CX-1,若CX≠0且ZF=1则转移 - LOOPNE/LOOPNZ 标号:CX=CX-1,若CX≠0且ZF=0则转移 - JCXZ 标号:若CX=0则转移

子程序调用与返回: - CALL 目标:调用子程序

  1. 近调用:将IP压栈
  2. 远调用:将CS和IP压栈

- RET [n]:从子程序返回

  1. 近返回:从栈弹出IP
  2. 远返回:从栈弹出IP和CS
  3. 可选的n表示返回后再将SP加上n(用于清除参数)

顺序结构是最基本的程序结构,指令按顺序逐条执行。

【例题】计算表达式:Z = (X + Y) × (X - Y) ``` DATA SEGMENT

  X DW 50
  Y DW 30
  Z DW ?

DATA ENDS

CODE SEGMENT

  ASSUME CS:CODE, DS:DATA

START:

  MOV AX, DATA
  MOV DS, AX
  
  MOV AX, X       ;AX = X
  ADD AX, Y       ;AX = X + Y
  MOV BX, AX      ;BX = X + Y
  
  MOV AX, X       ;AX = X
  SUB AX, Y       ;AX = X - Y
  
  IMUL BX         ;DX:AX = (X+Y) × (X-Y)
  MOV Z, AX       ;假设结果不溢出,只保存低16位
  
  MOV AH, 4CH
  INT 21H

CODE ENDS END START ```

分支结构根据条件判断选择不同的执行路径。

【例题】求两个有符号数中的较大值。 ``` DATA SEGMENT

  NUM1 DW -100
  NUM2 DW 50
  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 GREATER     ;若NUM1 ≥ NUM2,跳转到GREATER
  MOV AX, NUM2    ;否则,AX = NUM2

GREATER:

  MOV MAX, AX     ;保存较大值
  
  MOV AH, 4CH
  INT 21H

CODE ENDS END START ```

【例题】根据AL中的值(0-9)转移到不同的处理程序。 ```

  CMP AL, 0
  JB ERROR
  CMP AL, 9
  JA ERROR
  
  MOV BX, OFFSET JUMP_TABLE
  MOV AH, 0
  SHL AX, 1       ;AX = AL × 2(字表索引)
  ADD BX, AX
  JMP [BX]        ;跳转到对应的处理程序

JUMP_TABLE:

  DW HANDLE_0
  DW HANDLE_1
  ; ...
  DW HANDLE_9

```

循环结构用于重复执行某段代码。

【例题】计算1 + 2 + 3 + … + 100。 ``` DATA SEGMENT

  SUM DW ?

DATA ENDS

CODE SEGMENT

  ASSUME CS:CODE, DS:DATA

START:

  MOV AX, DATA
  MOV DS, AX
  
  MOV AX, 0       ;累加器清零
  MOV CX, 100     ;循环计数器
  MOV BX, 1       ;当前加数

LOOP_START:

  ADD AX, BX      ;累加
  INC BX          ;加数加1
  LOOP LOOP_START ;CX减1,若不为0则继续循环
  
  MOV SUM, AX
  
  MOV AH, 4CH
  INT 21H

CODE ENDS END START ```

【例题】在数组中查找最大值。 ``` DATA SEGMENT

  ARRAY DW 23, 56, 12, 89, 34, 78, 45, 67
  COUNT EQU ($ - ARRAY) / 2
  MAX DW ?

DATA ENDS

CODE SEGMENT

  ASSUME CS:CODE, DS:DATA

START:

  MOV AX, DATA
  MOV DS, AX
  
  LEA SI, ARRAY
  MOV AX, [SI]    ;假设第一个元素最大
  MOV CX, COUNT-1 ;比较次数
  ADD SI, 2       ;指向下一个元素

FIND_MAX:

  CMP AX, [SI]    ;比较当前最大值与下一个元素
  JGE NEXT
  MOV AX, [SI]    ;更新最大值

NEXT:

  ADD SI, 2
  LOOP FIND_MAX
  
  MOV MAX, AX
  
  MOV AH, 4CH
  INT 21H

CODE ENDS END START ```

子程序(过程)是完成特定功能的独立代码段,可以被多次调用。

【例题】编写子程序计算N的阶乘(N!)。 ``` ;子程序:计算N的阶乘 ;入口:CX = N(N ≤ 8,确保结果在16位内) ;出口:AX = N! FACTORIAL PROC

  PUSH BX
  PUSH CX
  
  MOV AX, 1       ;阶乘结果初始化为1

FACT_LOOP:

  CMP CX, 1
  JLE FACT_DONE
  MUL CX          ;AX = AX × CX
  LOOP FACT_LOOP
  

FACT_DONE:

  POP CX
  POP BX
  RET

FACTORIAL ENDP ```

【例题】主程序调用阶乘子程序计算5! + 6!。 ``` DATA SEGMENT

  RESULT DW ?

DATA ENDS

CODE SEGMENT

  ASSUME CS:CODE, DS:DATA

START:

  MOV AX, DATA
  MOV DS, AX
  
  MOV CX, 5
  CALL FACTORIAL  ;计算5!,结果在AX
  MOV BX, AX      ;保存5!
  
  MOV CX, 6
  CALL FACTORIAL  ;计算6!,结果在AX
  
  ADD AX, BX      ;AX = 5! + 6!
  MOV RESULT, AX
  
  MOV AH, 4CH
  INT 21H

FACTORIAL PROC

  ;...(同上)

FACTORIAL ENDP

CODE ENDS END START ```

在系统编程和性能关键代码中,经常需要在C语言程序中嵌入汇编代码,或将汇编语言编写的模块与C语言程序链接。

内联汇编(Inline Assembly):在C/C++代码中直接嵌入汇编指令。

GCC内联汇编格式: ```c asm [volatile] (“汇编指令”

              : 输出操作数
              : 输入操作数
              : 被破坏的寄存器);

```

示例: ```c int a = 10, b = 20, result; asm volatile (

  "addl %%ebx, %%eax"
  : "=a" (result)     ;输出:result = EAX
  : "a" (a), "b" (b)  ;输入:EAX = a, EBX = b

); ```

独立汇编模块:将汇编代码编译成目标文件,然后与C程序链接。

汇编模块示例(x86-64,System V AMD64 ABI): ```asm ; add.asm - 实现加法函数 section .text global add_numbers

add_numbers:

  mov rax, rdi    ;第一个参数在RDI
  add rax, rsi    ;第二个参数在RSI
  ret             ;结果在RAX返回

```

对应的C程序: ```c main.c extern int add_numbers(int a, int b); int main() { int result = add_numbers(10, 20); return 0; } ``` ==== 7.6.2 调用约定 ==== 调用约定规定了函数调用时参数的传递方式、寄存器的使用规则和栈的清理责任。 x86-32常用调用约定: - cdecl:参数从右向左压栈,调用者清理栈,返回值在EAX - stdcall:参数从右向左压栈,被调用者清理栈,Win32 API使用 - fastcall:前两个参数通过ECX和EDX传递,其余压栈 x86-64 System V AMD64 ABI(Linux/Unix): - 参数通过寄存器传递:RDI, RSI, RDX, RCX, R8, R9 - 额外的参数压栈 - 返回值在RAX(64位)或RAX:RDX(128位) - 被调用者保存的寄存器:RBX, RBP, R12-R15 x86-64 Windows x64调用约定: - 参数通过寄存器传递:RCX, RDX, R8, R9 - 调用者分配32字节的“阴影空间” - 返回值在RAX ===== 7.7 本章重点总结 ===== 核心概念: - 汇编语言是机器语言的符号化表示,与机器语言一一对应 - 汇编语言源程序由指令语句、伪指令语句和宏指令组成 - 汇编程序的功能是将汇编语言翻译成机器语言 关键语法: - 数据定义伪指令:DB、DW、DD、DUP - 符号定义伪指令:EQU、= - 段定义伪指令:SEGMENT、ENDS、ASSUME - 过程定义伪指令:PROC、ENDP 指令分类: - 数据传送:MOV、PUSH/POP、XCHG、LEA、XLAT - 算术运算:ADD、SUB、MUL、DIV、INC、DEC、CMP - 逻辑运算:AND、OR、XOR、NOT、TEST - 移位:SHL、SHR、SAR、ROL、ROR、RCL、RCR - 控制转移:JMP、Jcc、CALL、RET、LOOP 程序结构: - 顺序结构:按顺序执行 - 分支结构:根据条件选择路径 - 循环结构:重复执行代码块 - 子程序结构:模块化设计,代码复用 混合编程: - 内联汇编:在C/C++中嵌入汇编代码 - 独立模块:汇编模块与C程序链接 - 调用约定:规定参数传递和寄存器使用规则 学习建议: - 掌握寻址方式和指令系统 - 熟悉常用伪指令的用法 - 练习编写基本程序结构(顺序、分支、循环、子程序) - 理解汇编与高级语言的接口机制

该主题尚不存在

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

  • 计算机组成与体系结构/汇编语言基础.txt
  • 最后更改: 2026/03/01 16:22
  • 张叶安