汇编语言程序设计
进制与编码
数字逻辑存储和寄存器
存储和内存布局
存储单位
| 单位 | 长度 | 二进制位 |
|---|---|---|
| 字节(基本单位) | 1 字节 | 8 |
| 字 | 2 字节 | 16 |
| 双字 | 4 字节 | 32 |
| 四字 | 8 字节 | 64 |
| 节 | 16 字节 | 128 |
| 页 | 256 字节 | 2048 |
地址
- 概念:为了标识和存取每一个存储单元,给每个存储单元规定一个编号,这个编号就是地址
- 内容:一个存储单元中存放的信息称为该存储单元的内容
- 低字节存入低地址,高字节存入高地址
小端方式:低对低、高对高的存储模式
- 每一个字节单元有一个唯一的存储地址
- 数据从高地址到低地址读取
- 数组数据按照顺序从低地址到高地址存储
- 多字节数据用低地址表示
例子
| 高字节 | 低字节 | 低字节地址 |
|---|---|---|
对于 单元:
- 若按照字节来读取,则其值为
- 若按照字来读取,则其值为
例子
对于 数据 'AB':
若按照字节存储(数组):
1 | MESSAGE DB 'AB' |
其为数组类型,A存入低字节,B存入高字节。
其存储格式为:
| 高字节 | 低字节 |
|---|---|
若按照字存储:
1 | MESSAGE DW 'AB' |
其不为数组类型,则认为它是一个整体(同普通数字的存储),A存入高字节,B存入低字节。
其存储格式为:
| 高字节 | 低字节 |
|---|---|
地址分段
解决的问题:在
16位字长机器提供20位地址。分段方法:段地址+段内偏移
段地址:
- 起始地址为 16 的倍数
- 每段最大长度为
1M寻址空间中,可分为 个逻辑段
逻辑地址(32 位):用两个字表示,书写格式为
段地址:偏移地址,存储格式为:
| 段地址(一个字) | 偏移地址(一个字) |
- 物理地址(20 位):
例子
若一个逻辑地址为,则其物理地址为
地址对齐
原因:
- 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的,某些平台只支持指定位数的地址。比如某 16 位平台只能按照字来读取数据
- 性能原因:数据结构(如结构体)应该尽可能地在自然边界上对齐。为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
对齐方式:
- 字单元安排在偶地址
- 双字单元安排在模 4 地址
优点:
- 简化了内存和 CPU 之间接口的设计。
- 减少了额外的寻址开销和数据处理开销。
例子
对于 16 位机器,它一次性读取的数据为 2 个字节。在某些平台上,它只能按照字来读取数据。
若放入奇地址,则为了读取这个数据,它必须分成两次来读取,而且得到数据还有额外的计算开销:

若放入偶地址,则为了读取这个数据,只需要一次来读取:

寄存器
- 介绍:一个寄存器相当于运算器中的一个存储单元,其存储速度远大于主存。
- 功能:存放操作数地址、操作数和运算的中间结果。
数据寄存器
| 名称 | 英文全称 | 32 位版本 | 16 位版本 | 8 位版本 | 功能 |
|---|---|---|---|---|---|
| A | Accumulator | EAX | AX | AH,AL | 累加器 |
| B | Base | EBX | BX | BH,BL | 基址变址 |
| C | Count | ECX | CX | CH,CL | 计数器 |
| D | Data | EDX | DX | DH,DL | 通用寄存器 |
指针寄存器
| 名称 | 英文全称 | 32 位版本 | 16 位版本 | 8 位版本 | 功能 |
|---|---|---|---|---|---|
| SP | Stack Pointer | ESP | SP | 没有 | 堆栈指针 |
| BP | Base Pointer | EBP | BP | 没有 | 基址指针 |
变址寄存器
| 名称 | 英文全称 | 32 位版本 | 16 位版本 | 8 位版本 | 功能 |
|---|---|---|---|---|---|
| DI | Destination Index | EDI | DI | 没有 | 目的变址 |
| SI | Source Index | ESI | SI | 没有 | 源变址 |
控制寄存器
| 名称 | 英文全称 | 32 位版本 | 16 位版本 | 8 位版本 | 功能 |
|---|---|---|---|---|---|
| IP | Instruction Pointer | 没有 | IP | 没有 | 指令寄存器,存放下一条命令的地址 |
| FLAGS | 没有 | 没有 | FLAGS | 没有 | 标志 |
常用标志:
| 标志 | 英文全称 | 解释 |
|---|---|---|
| OF | Overflow Flag | 溢出标志 |
| SF | Sign Flag | 符号标志 |
| ZF | Zero Flag | 零标志 |
| CF | Carry Flag | 进位标志 |
| AF | Auxiliary Flag | 辅助进位标志 |
| PF | Parity Flag | 奇偶标志 |
| DF | Direction Flag | 方向标志 |
| TF | Trap Flag | 陷阱标志 |
| IF | Interrupt Flag | 中断标志 |
| IOPL | I/O Privilege Level | I/O 特权级 |
段寄存器
| 名称 | 英文全称 | 32 位版本 | 16 位版本 | 8 位版本 | 功能 |
|---|---|---|---|---|---|
| CS | Code Segment | 没有 | CS | 没有 | 代码段,存放正在运行的程序 |
| DS | Data Segment | 没有 | DS | 没有 | 数据段,存放当前程序使用的数据 |
| ES | Extra Segment | 没有 | ES | 没有 | 附加段,辅助的数据区 |
| SS | Stack Segment | 没有 | SS | 没有 | 堆栈段,特殊的存储区,通过后进先出原则访问数据 |
寻址
寻址就是找到指令中操作数所在的位置。寻址后可以获取操作数的内容。
数据寻址
数据可能在这些位置:
- 指令代码直接包含了操作数
- CPU 中的寄存器
- I/O 端口
- 存储器
| 寻址方式 | 操作数位置 | 示例 | 注意 |
|---|---|---|---|
| 立即寻址 | 当前执行的指令,在代码段 CS 中。 | MOV AL,5,其中 5 是立即寻址 | 不能作为指令的目的操作数 |
| 寄存器寻址 | 寄存器 | MOV AL,5,其中 AL 是寄存器寻址 | 暂无 |
| 直接寻址 | 立即数所给的位置 | MOV AX,[2000H],其中 [2000H] 是直接寻址,直接从存储器中找到偏移地址为 2000H的单元 | 暂无 |
| 间接寻址 | 某个寄存器所给的位置 | MOV AX,[BX],其中 [BX] 是间接寻址,先获取BX的内容,再以BX的内容作为地址寻找数据所在位置 | 1.不能用 AX、CX、DX 存放地址2.可用寄存器: BX、BP、DI和SI3.一般用于数组、字符串和表格处理 |
| 相对寻址(直接变址) | 某个寄存器所给的位置和指令中指定的位移量之和 | MOV AX,COUNT[SI],其中 COUNT[SI] 是相对寻址,地址值为 COUNT 和 SI 之和 | 1.不能用 AX、CX、DX 存放地址2.可用寄存器: BX、BP、DI和SI3.一般用于数组下标访问 |
| 基址变址 | 基址寄存器和变址寄存器的内容之和 | MOV AX,[BX][SI],其中 [BX][SI] 是基址变址,地址值为 COUNT 和 SI 之和 | 1.基址寄存器:16 位只能使用 BX和 BP,32 位可以使用任意 32 位通用寄存器2.变址寄存器:16 位只能使用 SI和 DI,32 位可以使用 ESP 外的任意寄存器 |
| 相对基址变址 | 某个寄存器所给的位置、基址寄存器和变址寄存器的内容之和 | MOV AX,COUNT[BX][SI],其中 COUNT[BX][SI] 是相对基址变址,地址值为 COUNT 和 SI、BX 之和 | 1.基址寄存器:16 位只能使用 BX和 BP,32 位可以使用任意 32 位通用寄存器2.变址寄存器:16 位只能使用 SI和 DI,32 位可以使用 ESP 外的任意寄存器 |
转移寻址
| 寻址方式 | 段地址(存入) | 偏移地址(存入) | 示例 | 注意 |
|---|---|---|---|---|
| 段内直接寻址 | 不变 | 操作数的偏移地址 | JMP NEAR PTR ERROR | 短跳转:位移量为 8 位,符号前加 SHORT近跳转:位移量为 16 位,符号前加 NEAR PTR |
| 段内间接寻址 | 不变 | 操作数的值 | JMP WORD PTR [ERROR] | |
| 段间直接寻址 | 操作数的段地址 | 操作数的偏移地址 | JMP FAR PTR ERROR | |
| 段间间接寻址 | 操作数的高字 | 操作数的低字 | JMP DWORD PTR [ERROR] | ERROR 是一个双字数据 |
程序格式
汇编源程序格式
概述
- 编写一个汇编源程序,需要做以下几件事:
- 编写数据段
- 编写代码段,指定代码入口
- 使用
ASSUME指定各段与寄存器的关系 - 将各段装入相应的寄存器
- 编写程序主代码
- 结束程序
- 汇编结束,并指定程序执行的起点
1 | ; 数据段定义 |
数据分段
- 代码段:
- 概念:代码段用来存放程序的指令序列
- 段寄存器:
- 寻址:
- 堆栈段:
- 概念:堆栈段确定堆栈所在的主存区域
- 段寄存器:
- 寻址:
- 数据段:
- 概念:数据段存放当前运行程序所用的数据
- 段寄存器:
- 寻址:, 为数据的偏移地址
- 附加段:
- 概念:附加的数据段,也用于数据的保存
- 段寄存器:
- 寻址: , 为数据的偏移地址
表达式
表达式可以作为各指令的操作数。其值可以在汇编的时候确定。
标号/变量:存储的是地址,拥有段值、偏移量、类型三种属性
- 标号、子程序名的类型可以是 NEAR(近)和 FAR(远),分别表示段内或段间
- 变量名的类型可以是 BYTE(字节)、WORD(字)和 DWORD(双字)等
表达式:数字表达式 地址表达式
算术操作符: + 、- 、*、/、mod
逻辑和移位运算符:AND、OR、XOR、NOT、SHL(左移位)、SHR(右移位)
关系运算符:EQ(相等)、NE(不相等)、LT(小于)、LE(小于等于)、GT(大于)、GE(大于等于)
- 真的结果为 0FFFFH
- 假的结果为 0000H
数值回送运算符:
| 运算符 | 说明 |
|---|---|
| SEG expression | 分离出其后变量或标号所在段的段地址 |
| OFFSET expression | 分离出其后变量或标号所在段的偏移地址 |
| TYPE expression | 如果是变量,将返回该变量的类型对应字节数; 如果是标号,则返回代表标号类型的数值(NEAR=-1,FAR=-2)。 |
| LENGTH expression | 取出变量所含的数据存储单元个数。 对于 DUP 定义的变量,返回重复的次数,否则返回 1 |
| SIZE expression | 等于 LENGTH 变量 * TYPE 变量 |
示例
1 | DATA SEGMENT |
- 属性操作符
- PTR:
type PTR expression用于指定表达式的类型,type 有byte,word等 - 段操作符:
段名:表达式用于指定一个变量或标号的段地址 - SHORT:用于指定转移地址的属性
- THIS:类似 PTR ,可以建立指定类型或指定距离的地址操作数,其段地址和偏移地址和下一个存储地址相同
- HIGH、LOW、HIGHWORD、LOWWORD:用于分离高字节、低字节、高字、低字
- PTR:
类型
- 数据类型
- 汇编的数据类型只按照其大小进行区分,不区分有无符号、字符和整数
| 数据类型 | 大小LENGTH 变量 |
|---|---|
BYTE | 1 |
WORD | 2 |
DWORD | 3 |
FWORD | 4 |
QWORD | 8 |
TWORD | 10 |
- 标号和子程序的类型
- 标号
标号:,表示一行代码的起始地址 - 标号和子程序的类型只区分要跳转的代码距离当前正在执行的代码的远近
- 标号
| 数据类型 | 大小LENGTH 变量 | 说明 |
|---|---|---|
NEAR PTR | -1 | 近地址类型 |
FAR PTR | -2 | 远地址类型 |
伪操作
概述
伪操作是汇编程序对源程序进行汇编时处理的操作,完成处理器选择、存储模式定义、数据定义、存储器分配、指示程序开始结束等功能。
- 处理器选择伪操作
- 段定义伪操作
- 存储模式伪操作
- 程序开始和结束伪操作
- 数据定义及存储器分配伪操作
- 表达式赋值伪操作
- 地址计数器与对准伪操作
- 基数控制伪操作
处理器选择
| 指令 | 说明 |
|---|---|
| .8086 | (默认)选择 8086 指令系统 |
| .286 | 选择 80286 指令系统 |
| .286P | 选择保护模式下的 80286 指令系统 |
| .386 | 选择 80386 指令系统 |
| .386P | 选择保护模式下的 80386 指令系统 |
| .486 | 选择 80486 指令系统 |
| .486P | 选择保护模式下的 80486 指令系统 |
| .586 | 选择 Pentium 指令系统 |
| .586P | 选择保护模式下的 Pentium 指令系统 |
段定义
1 | 段名 segment 定位 组合 段字 '类别' |
- 定位:指定逻辑段在主存储器中的边界
| 值 | 说明 |
|---|---|
| PARA | (默认) 表示本段必须从能被 16 整除的地址处开始存放,即段起始地址最低四位必须是 0 |
| WORD | 表示本段要从一个偶数地址处开始存放,即段起始地址的最低一位必须是 0 |
| BYTE | 表示本段起始地址可以从任一地址处开始存放 |
| PAGE | 表示本段要从能被 256 整除的地址处开始存放,即起始地址的最低八位必须是 0 |
- 组合:指定多个逻辑段之间的关系
| 值 | 说明 | 注意 |
|---|---|---|
| PRIVATE | (默认) 本段与其他段没有逻辑关系,不与其他段合并,每段都有自己的段地址 | |
| PUBLIC | 连接程序把本段与所有同名同类型的其他段相邻地连接在一起,然后为所有这些段指定一个共同的段地址,也就是合成一个物理段 | |
| STACK | 本段是堆栈的一部分,连接程序将所有 STACK 段按照与 PUBLIC 段的同样方式进行合并。 | 堆栈段必须具有该段组合 |
| COMMON | 把该段和与该段名称相同的其他段重叠在一起形成一个重叠段(多个逻辑段拥有相同的段基值),重叠段的长度取决于其中最长逻辑段的长度。类似 C 的共用体。 | |
| MEMORY | 表示该段的起始地址位于其他所有段的后面(在程序中位于最高内存地址一端)。 | 只有第一个被解释的 MEMORY 段作为 MEMORY 组合类型,后面的 MEMORY 段全部被解释为 COMMON 段 |
| AT 表达式 | 使用这种类型可以自己定义段的起始地址 | 表达式的取值只能是 16 位二进制数,且低四位必须为 0 |
类别:
- 当连接程序组织段时,将所有的同类别段相邻分配
- 段类别可以是任意名称,但必须位于单引号中
ASSUME 伪指令:指定各段和寄存器的关系,建立段寄存器与段的缺省关系。
- 汇编程序会根据数据所在的逻辑段,在需要时自动插入段超越前缀。
- ASSUME 伪指令并不为段寄存器设定初值,仅指定对应关系。
ASSUME伪指令可以重复使用,影响声明后的代码。
1 | ASSUME register_name:segment_name,... |
例子
假设有 ASSUME 伪指令:
1 | ASSUME DS:DATAS |
则它告诉编译器,在以后使用 DATAS 段中定义的符号时,默认使用 DS 寄存器作为段地址。
- 段寄存器的装填:在程序开始执行时,需要对寄存器进行装填。
- CS 由系统自动装填
- DS、ES、SS(未使用 STACK 作为组合类型的堆栈段)则由用户自行装填。
1 | MOV AX,逻辑段名 |
- 段组伪指令:把多个同类段合并为一个 64KB 物理段
- 定义段组后,段组内各段就统一为一个段地址,各段定义的变量和标号的偏移地址就相对于段组基地址计算。
offset操作符取变量和标号相对于段组的偏移地址,如果没有段组则取得相对于段的偏移地址。- 每个段的偏移地址是按照段组中段顺序来的
offset后可以跟段组中的某个段名,表示该段最后一个字节后面字节相对于段组的偏移地址。
1 | group_name GROUP segment1,segment2,... |
例子
1 | data1 segment word |
若 ASSUME DS:datagroup
则:
MOV BX,offset data1时,MOV BX,offset const1时,MOV BX,offset data2时,MOV BX,offset var1时,
程序的开始
NAME作为模块名称TITLE作为列表文件每一页上打印的标题,最大长度为 60 个字符- 两种定义的关系:
- 如果没有指定
NAME,则text前六个字符作为模块名 - 如果没有指定
TITLE,则使用文件名作为模块名
- 如果没有指定
1 | NAME module_name |
.STARTUP伪操作:产生程序开始执行的代码- 按照 CPU 类型、存储模式、操作系统和堆栈类型,产生程序开始执行的代码
- 同时还指定程序开始执行的起始点
- 使用了
.STARTUP则源程序结束时不需要指定程序运行起点
程序的结束
结束汇编(源程序结束)
- 格式:
[LABEL]用于指定程序运行的起点
1 | END [LABEL] |
结束运行
- 通过
RET进行结束
1 | code segment |
- 通过中断进行结束
1 | code segment |
- 产生结束的代码:可选参数是一个返回的数码,通常用 0 表示没有错误
- 此伪指令会自动产生结束运行的代码
1 | .EXIT [返回参数] |
数据定义
1 | [ 变量名 ] 大小标识 操作数项 [ ,操作数项,… … ] |
- 大小标识
| 大小标识 | 大小 |
|---|---|
| DB | 1B |
| DW | 2B(字) |
| DF | 3B |
| DD | 4B(双字) |
| DQ | 8B(四字) |
| DT | 10B |
- 操作数项
| 类型 | 解释 | 示例 |
|---|---|---|
| 数字 | 为一个或连续的存储单元设置数值初值 | INT_VAR DB 13H |
| 字符串 | 必须用引号引起来,将字符串中的各字符均以 ASCⅡ 码形式存放在相应的存储单元 | STR_VAR DB 'HELLO WORLD$' |
| 带标识符的表达式 | 适用于 DW 和 DD 对于 DW,则存储偏移地址 对于 DD,则高位存储段地址,低位存储偏移地址 | ADDR_VAR DW INT_VAR+2将 INT_VAR 的偏移地址加 2,赋值给 ADDR_VAR |
| ? | 不赋初始值 | ADDR_VAR DW ? |
N DUP(初值,...) | 为连续的存储单元提供重复数据 N 为重复次数 | BUF DB 100 DUP (0)定义一个大小为 100 个字节,初值为 0 的数据存储单元 |
例子
对于 数据 'AB':
若按照字节存储(数组):
1 | MESSAGE DB 'AB' |
其为数组类型,A存入低字节,B存入高字节。
其存储格式为:
| 高字节 | 低字节 |
|---|---|
若按照字存储:
1 | MESSAGE DW 'AB' |
其不为数组类型,则认为它是一个整体(同普通数字的存储),A存入高字节,B存入低字节。
其存储格式为:
| 高字节 | 低字节 |
|---|---|
- 多行变量的赋值
- 前一行尾不可加逗号
- 后续行不可丢伪操作符
例子
1 | array db 1,2,3,4,5 |
- LABEL 伪操作:定义别名
- label 操作后面跟的数据定义即为其别名
- 等价操作:
EQU THIS
1 | 标识符 label 大小标识 |
例子
1 | my_array label dw |
则 array 和 my_array 代表了同一片存储空间,但 array 是字节,my_array 是字。
表达式赋值
EQU 伪操作
1 | expression_name EQU expression |
- 先定义后使用,且不能重复定义
- 表达式可以为常数、数值表达式、地址表达式、变量名、标号、寄存器名称、指令助记符等
- 源程序中的所有符号名都会被替换为它代表的内容,类似 C 语言的宏常量
例子
1 | a equ var+4 |
则 mov ax,a 汇编时变成 mov ax,var+4。
= 伪操作
1 | 符号名 = 表达式 |
- 等号语句的作用和等值语句完全一致,区别是已经用等号定义过的符号可以再次使用等号修改其定义。
- EQU 伪操作和等号伪操作不能同时使用
地址计数与地址对齐
地址计数器 $
- 保存当前正在汇编的指令的偏移地址
- 同一个数据定义中,
$的值不变
例子
1 | DATAS SEGMENT |
则 B_VAR 开始的 3 个 DW 值为 1,2,3
ORG 对齐
- 使紧跟其后的单元偏移地址为常数表达式计算出的值
- ORG 会修改地址计数器的值
- 特殊别名:
EVEN:使下一地址从偶地址开始ALIGN boundary:从 boundary 的整数倍地址开始
例子
1 | DATAS SEGMENT |
A_VAR 的偏移地址为 0,B_VAR的偏移地址为 20H,B_VAR开始的 3 个 DW 值为 20H,21H,22H
基数控制
1 | .RADIX EXPRESSION |
- 规定源程序中无标记数的基数( 2、8、10、16 选一)
宏汇编和重复汇编
宏汇编
宏定义和宏调用
- 宏定义
- params1,params2,… 是哑元,可省
1 | macro_name MACRO params1,params2,... |
- 宏调用
- params1,params2,… 是实元
1 | macro_name params1,params2,... |
- 宏必须先定义后调用
哑元和实元
- 哑元:相当于函数的形参
- 实元:相当于函数的实参
- 参数和操作码的连接:
&
示例
1 | ; 宏定义 |
- 在宏调用中,
%用于将表达式的值转换成当前基数下的数,而不是直接把表达式代入哑元
示例
1 | ; 宏定义 |
宏定义中的标号
1 | LOCAL SIGN1,SIGN2,... |
- LOCAL 操作符:宏展开时,LOCAL 操作符中出现的标号会变成全局唯一的符号。从而避免宏展开时,重名标号导致的报错。
1 | LOCAL_MACRO MACRO CONDITION,FUNC |
宏定义中的注释
- 指示在
.lst中,宏如何展开的伪操作:
| 伪操作 | 说明 |
|---|---|
| .XALL | (默认) 只展开代码,不展开单双分号注释 |
| .SALL | 不列出任何展开信息 |
| .LALL | 只展开代码,不展开双分号注释 |
- 双分号注释:
;;在任何时候都不展开 - 单分号注释 :
;在.XALL模式下展开
宏库
- 概念:把常用的宏建立成独立的文件
- 定义宏库:以
.mac或.inc结尾 - 使用宏库:
INCLUDE 路径- 必须放在宏调用之前
删除宏
- PURGE 指令:
1 | PURGE MACRO1,MACRO2,... |
重复汇编
REPT
- 语法
1 | REPT expression |
expression的值将作为重复次数
IRP
- 用于把重复的代码按照参数进行重复。
- 重复时,每个参数都会赋值给
dummy(哑元,同宏里面的哑元)
1 | IRP dummy,<argument1,argument2,argument3,...> |
示例
1 | ; 重复定义 |
IRPC
- 用于把重复的代码按照参数进行重复。
- 参数是一个字符串,重复时,会按照字符赋值给
dummy(哑元,同宏里面的哑元)
1 | IRPC dummy,string |
示例
1 | ; 重复定义 |
常用运算
传送
通用传送
(数据传送:MOV)
格式
1 | MOV DST,SRC |
注意
- 不能同时为存储器
- 不能同时为段寄存器
DST不能存放立即数DST不能为CS- 两个操作数大小必须一致
相当于 C 语言的赋值
1 | int a; |
(地址传送:LEA)
格式
1 | LEA DST,SRC |
注意
- 不能使用立即数
- 不能使用段寄存器
- 若地址长度不一致,会进行低位截取 和 零位拓展 操作。
相当于 C 语言的取地址后赋值
1 | int a; |
(数据交换:XCHG)
格式
1 | XCHG DST,SRC |
注意
- 不能使用立即数
- 不能使用段寄存器
- 不能同时为存储器
CS不能作为操作数
标志传送
LAHF:把 FLAGS 的低字节送入 AH
SAHF:把 AH送入 FLAGS的低字节
PUSHF:标志进栈
POPF:标志出栈
向其他硬件传送和获取数据
在里,每一个 I/O 设备都有固定的端口,CPU 和 I/O 设备的通信都是通过端口来完成的。
注意:
NUM只能使用AL和AX传送数据。
| 指令 | 说明 |
|---|---|
IN NUM,PORT | 从端口为 PORT 的硬件获得数据,传送给 NUM |
OUT NUM,PORT | 向端口为 PORT 的硬件传入 AL的数据 |
XLAT NUM,PORT | 端口为 PORT 的硬件和 AL交换数据 |
堆栈操作
概述
概述:堆栈是一个特殊存储区,用于临时存放数据。
结构:
- 后进先出,只有一个出入口。
- 栈底位于高地址,栈顶位于底地址。
基本操作:入栈、出栈
相关寄存器: SS为堆栈段寄存器,存放栈顶的段地址SP存放栈顶的偏移地址- 访问栈顶:
SS:SP
标志位:不影响任何标志位
堆栈操作
堆栈操作在位指令中必须以字为单位,在位指令中必须以双字为单位。堆栈操作只能以字或双字为单位进行操作。
堆栈操作不影响标志位
(PUSH:入栈)
格式
1 | PUSH SRC |
注意:
- 不能使用立即数
PUSH SP时,传入的是已改变的新值;而PUSH ESP传入的是旧值。
入栈时,SP会向低地址偏移,偏移量与操作数相同
示例
1 | MOV AX, H |

(POP:出栈)
格式
1 | POP DST |
注意:
- 不能使用立即数
- 操作数不能为
CS和IP
出栈时,SP 会向高地址偏移,偏移量与操作数相同
示例
1 | POP AX |

算术运算
加减法
指令
| 指令 | 操作 | 进位(CF)置位条件 | 溢出(OF)置位条件 | 说明 |
|---|---|---|---|---|
ADD DST, SRC | 和的最高有效位有向高位的进位 | 两个操作数符号相同,而结果符号与之相反 | 加法 | |
ADC DST, SRC | 和的最高有效位有向高位的进位 | 两个操作数符号相同,而结果符号与之相反 | 带进位加法 | |
INC DST | $DST=DST+ | 和的最高有效位有向高位的进位 | 两个操作数符号相同,而结果符号与之相反 | 加 |
SUB DST, SRC | 被减数的最高有效位有向高位的借位 即无符号数 DST < SRC | 两个操作数符号相反,而结果的符号与减数相同 | 减法 | |
SBB DST, SRC | 被减数的最高有效位有向高位的借位 即无符号数 DST < SRC + 两个操作数符号相反,而结果的符号与减数相同 | 带借位减法 | ||
DEC DST | $DST=DST- | 被减数的最高有效位有向高位的借位 | 两个操作数符号相反,而结果的符号与减数相同 | 减 |
NEG DST | 操作数不为操作数为 -(字节运算)或操作数为 -(字运算) | 求补 |
理解
加法中,结果进位和溢出的判断
负数+正数:不会溢出
正数+负数:不会溢出
正数+正数:理论上结果为正才是正确,若出现了负数,就是溢出
负数+负数:理论上结果为负才是正确,若出现了正数,就是溢出
减法中,结果进位和溢出的判断
负数-正数:理论上结果为负数时才是正确,若出现了正数,就是溢出
正数-负数:理论上结果为正数时才是正确,若出现了负数,就是溢出
正数-正数:不会溢出
负数-负数:不会溢出
乘法
指令
| 指令 | 操作 | 说明 |
|---|---|---|
MUL SRC | 字节操作数 字操作数 | 无符号乘法 |
IMUL SRC | 字节操作数 字操作数 | 有符号乘法 |
标志位
| 标志位 | 无符号乘法 | 有符号乘法 |
|---|---|---|
| 进位 CF | 乘积的高一半为字为 DX,字节为 AH)时,其值为乘积的高一半是低一半的符号拓展(正数为负数为时,其值为 | |
| 溢出 OF | 乘积的高一半为字为 DX,字节为 AH)时,其值为乘积的高一半是低一半的符号拓展(正数为负数为时,其值为 | |
| 符号 SF | 两个数按照有符号数运算,结果为负数 | 两个数按照有符号数运算,结果为负数 |
| 零 ZF | 结果为结果为 |
除法
指令
| 指令 | 操作 | 说明 |
|---|---|---|
DIV SRC | 字节操作数 字操作数 | 无符号除法 |
IDIV SRC | 字节操作数 字操作数 | 有符号除法 |
标志位:除法对任何标志位没有定义
位运算
常见逻辑运算
运算
| 指令 | 操作 | 功能 | 说明 |
|---|---|---|---|
AND DST,SRC | DST=DST & SRC | 屏蔽位 | 与 |
TEST DST,SRC | DST & SRC | 测试某些位是否为测试,不保存结果 | |
OR DST,SRC | DST=DST | SRC | 保持某些位 | 或 |
NOT DST | DST=~DST | 将某数的各位按位取反 | 取反 |
XOR DST,SRC | DST=DST ^ SRC | 将某些位求反(同异或),其他位不变 | 异或 |
标志位:NOT 对任何标志位没有定义,其余的定义:
| 标志位 | 说明 |
|---|---|
| 进位 CF | 直接置 |
| 溢出 OF | 直接置 |
| 符号 SF | 结果为负数 |
| 零 ZF | 结果为 |
| 偶位 PF | 结果的二进制出现偶数个为出现奇数个 |
移位运算
所有移位指令中,
CNT只能为 CL
(算术移位)
算术移位指令适用于有符号数字的移位。
| 指令 | 说明 | 功能 | 示意图 |
|---|---|---|---|
SHL DST,CNT | 算术左移 | 将DST向左移动指定的次数最低位补入相应的 CF的内容为最后移入位的值 | ![]() |
SHR DST,CNT | 算术右移 | 将DST向右移动指定的次数最高位根据正负性补入 CF的内容为最后移入位的值 | ![]() |
(逻辑移位)
算术移位指令适用于无符号数字的移位。
| 指令 | 说明 | 功能 | 示意图 |
|---|---|---|---|
SAL DST,CNT | 逻辑左移 | 将DST向左移动指定的次数最低位补入相应的 CF的内容为最后移入位的值 | ![]() |
SHR DST,CNT | 逻辑右移 | 将DST向右移动指定的次数最高位补入相应的 CF的内容为最后移入位的值 | ![]() |
(循环移位)
| 指令 | 说明 | 功能 | 示意图 |
|---|---|---|---|
ROL DST,CNT | 循环左移 | 将目的操作数的最高位与最低位连成一个环 将环中的所有位一起向左移动规定的次数 CF 的内容为最后移入位的值 | ![]() |
ROR DST,CNT | 循环右移 | 将DST向右移动指定的次数最高位补入相应的 CF的内容为最后移入位的值 | ![]() |
RCL DST,CNT | 带进位循环左移 | 将目的操作数连同 CF 标志一起向左循环移动 CL 规定的次数 | ![]() |
RCR DST,CNT | 带进位循环右移 | 将目的操作数连同 CF 标志一起向右循环移动 CL 规定的次数 | ![]() |
类型转换和进制调整
类型转换指令
| 指令 | 说明 |
|---|---|
CBW | 字节转换成字,AL 按有符号整数转换成 AX |
CWD | 字转换成双字,AX 转换成 DX,AX |
类型转换操作
格式:XXX PTR 表达式
| 指令 | 说明 |
|---|---|
BYTE PTR | 将表达式转换成字节类型 |
WORD PTR | 将表达式转换成字类型 |
DWORD PTR | 将表达式转换成双字类型 |
FWORD PTR | 将表达式转换成类型 |
QWORD PTR | 将表达式转换成类型 |
NEAR PTR | 将表达式转换成近地址类型 |
FAR PTR | 将表达式转换成远地址类型 |
六.字符和字符串
字符和字符串概述
字符在内存中以 ASCII 的形式存储
字符串在内存中以 ASCII 数组的形式存储
单引号表示字符和字符串
通常以 $ 作为字符串的结尾
字符串的寻址方式
源操作数用寄存器SI寻址,默认在数据段DS中,但允许段超越。DS : [SI]
目的操作数用寄存器DI寻址,默认在附加段ES中,不允许段超越。ES : [DI]
每执行一次串操作指令,SI 和 DI 将自动修改:± 对于字节串)或 ± 对于字串)
执行指令 CLD 指令后,DF = 地址指针增
执行指令 STD 指令后,DF = 地址指针减
REP
| 指令 | 说明 |
|---|---|
REP MOVS / STOS / LODS | 先执行 CX=CX-若 CX=停止循环,否则继续循环 |
REPZ CMPS | 相等(为时重复 |
REPNZ | 不相等(不为时重复 |
执行操作:
如 (CX)=则退出 REP,否则转 2
(CX)=(CX) -1
执行 MOVS / STOS / LODS
重复 3
DF 标志
DF 用于控制字符串处理的方向。
| 方向 | 说明 |
|---|---|
| 方向向前 | |
| 方向向后 |
设置 DF 标志:
| 指令 | 说明 |
|---|---|
CLD | 设置 DF= |
STD | 设置 DF= |
字符串操作
字符串操作前的准备
- 源串起始地址 → SI
- 目的串起始地址 → DI
- 串长度 → CX
- 建立方向标志 DF
字符串传送
| 指令 | 说明 |
|---|---|
MOVS DST, SRC | 串传送操作,例如 MOVS ES: BYTE PTR [DI], DS: [SI] |
MOVSB | 字节串传送 |
MOVSW | 字串传送 |
执行操作:
传送值:((DI)) ← ((SI))
指针移动:(SI)←(SI)± 字操作为, (DI)←(DI)± 字操作为
方向标志 DF=时用 + ,DF=时用 -
字符串的存入(批量赋初值):通常用于初始化缓冲区
| 指令 | 说明 |
|---|---|
STOS DST | 串存入 |
STOSB | 字节串存入 |
STOSW | 字串存入 |
执行操作:
字节操作:((DI))←(AL), (DI)←(DI)±1
字操作:((DI))←(AX), (DI)←(DI)±2
从字符串取字符:
- LODS 指令一般不与 REP 联用
| 指令 | 说明 |
|---|---|
LODS SRC | 从串取 |
LODSB | 字节串存入 |
LODSW | 字串存入 |
字符串比较
| 指令 | 说明 |
|---|---|
CMPS DST, SRC | 串比较 |
CMPSB | 字符串比较 |
CMPSW | 字串比较 |
执行的操作:
((SI)) - ((DI)),并置标志位
字节操作:(SI)←(SI)± (DI)←(DI)±1
字操作: (SI)←(SI)± (DI)←(DI)±2字符串扫描
| 指令 | 说明 |
|---|---|
SCAS DST | 串扫描 |
SCASB | 字符串扫描 |
SCASW | 字串扫描 |
执行的操作:
- 字节操作:(AL) - ((DI)), (DI)←(DI)±1
- 字操作:(AX) - ((DI)), (DI)←(DI)±2
面向过程
输入和输出
字符输入和输出
- 字符输入
1 | MOV AH,1 |
从键盘输入字符的 ASCII 码送入寄存器 AL 中,并送显示器显示。
- 字符输出
1 | MOV DL,待显示字符的ASCII码 |
将 DL 寄存器中的字符送显示器显示。
字符串输入和输出
- 字符串输入
1 | LEA DX,缓冲区首偏移地址 |
缓冲区格式
第一个字节:缓冲区大小
第二个字节:实际输入字符数
第三个字节以后:缓冲区内容
1 | BUFFER DB 128,0,128 DUP(0) |
- 字符串输出
1 | LEA DX,待显示字符串首偏移地址 |
条件判断和跳转
条件判断
| 指令 | 说明 |
|---|---|
CMP OPR1,OPR1 | 执行 OPR1-OPR2,只置符号位,不保存结果 |
跳转指令
无条件跳转指令
| 指令 | 说明 |
|---|---|
JMP DST | 无条件跳转到某个标号 |
| 寻址方式 | 段地址(存入) | 偏移地址(存入) | 示例 | 注意 |
|---|---|---|---|---|
| 段内直接寻址 | 不变 | 操作数的偏移地址 | JMP NEAR PTR ERROR | 短跳转:位移量为 8 位,符号前加 SHORT近跳转:位移量为 16 位,符号前加 NEAR PTR |
| 段内间接寻址 | 不变 | 操作数的值 | JMP WORD PTR [ERROR] | |
| 段间直接寻址 | 操作数的段地址 | 操作数的偏移地址 | JMP FAR PTR ERROR | |
| 段间间接寻址 | 操作数的高字 | 操作数的低字 | JMP DWORD PTR [ERROR] | ERROR 是一个双字数据 |
Z 0 和相等的判断
由于两个数相等时,作差的结果为 0,所以等于的判断等同于 0 的判断
| 指令 | 跳转条件 | 说明 |
|---|---|---|
JZ DSTJE DST | ZF=1 | 结果为 0 则跳转 |
JZ DSTJNE DST | ZF=0 | 结果不为 0 则跳转 |
S 正负判断
| 指令 | 跳转条件 | 说明 |
|---|---|---|
JS DST | SF=1 | 结果为负则跳转 |
JNS DST | SF=0 | 结果非负则跳转 |
O 溢出判断
| 指令 | 跳转条件 | 说明 |
|---|---|---|
JO DST | OF=1 | 溢出则跳转 |
JNO DST | OF=0 | 未溢出则跳转 |
P 偶位判断
运算结果中,1 的个数为偶数个时,偶位为 1
| 指令 | 跳转条件 | 说明 |
|---|---|---|
JP DST | PF=1 | 偶位为 1 则跳转 |
JNP DST | PF=0 | 偶位为 0 则跳转 |
进位判断和无符号数的比较
| 进位指令 Carry | 小于指令 Below | 大于指令 Above | 跳转条件 | 说明 |
|---|---|---|---|---|
JC DST | JB DST | JNAE DST | CF=1 | 进位为 1 则跳转,或者两个无符号数比较DST < SRC |
JNC DST | JNB DST | JAE DST | CF=0 | 进位为 0 则跳转,或者两个无符号数比较DST >= SRC |
| 无 | JBE DST | JNAE DST | CF|ZF=1 | 两个无符号数比较DST <= SRC |
| 无 | JNBE DST | JA DST | CF|ZF=0 | 两个无符号数比较DST > SRC |
有符号数的比较
| 小于指令 Lower | 大于指令 Greater | 跳转条件 | 说明 |
|---|---|---|---|
JL DST | JNGE DST | SF^OF=1 | 两个有符号数比较DST < SRC |
JNL DST | JGE DST | SF^OF=0 | 两个有符号数比较DST >= SRC |
JLE DST | JNG DST | (SF^OF)|ZF=1 | 两个有符号数比较DST <= SRC |
JNLE DST | JG DST | (SF^OF)|ZF=0 | 两个有符号数比较DST > SRC |
理解 有符号数的小于比较
正数和正数比较:
正数和正数相减,不会出现溢出。但是若结果为负数,则为 小于 关系。负数和负数比较
- 负数和负数相减,不会出现溢出。但是若结果为负数,则为 小于 关系。
正数和负数比较
- 正数和负数相减,可能会出现溢出,且结果为负数。但这为 大于 关系。
负数和正数比较
- 负数和正数相减,可能会出现溢出,且结果为正数。但这为 小于 关系。
循环
循环指令
| 指令 | 循环条件 | 说明 |
|---|---|---|
LOOP DST | CX != 0 | 先进行CX=CX-1,再判断 CX 是否为 0,不为 0 则继续循环,否则退出循环 |
LOOPZ DST | CX != 0 && ZF == 0 | CX 不为 0,且 ZF 为 0 时进行循环,否则退出 |
LOOPNZ DST | CX != 0 && ZF != 0 | CX 不为 0,且 ZF 不为 0 时进行循环,否则退出 |
子程序
调用和返回指令
| 指令 | 说明 |
|---|---|
CALL DST | 调用某个标号 |
RET [EXP] | 返回,EXP 可指定额外弹出的字数 |
近调用的过程
PUSH IPMOV IP,IP+偏移量(直接近调用)
MOV IP,目标地址(间接近调用)近返回的过程
POP IP
远调用的过程
PUSH CSPUSH IPMOV CS,目标的段地址MOV IP,目标的偏移地址
远返回的过程
POP IPPOP CS
带立即数 n 的返回:除弹出
IP(和CS)后,再弹出 n 个字节。
- 标题: 汇编语言程序设计
- 作者: ObjectKaz
- 创建于: 2022-01-01 07:08:31
- 更新于: 2023-05-25 17:17:44
- 链接: https://www.objectkaz.cn/1cfcc37ed09b.html
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。





