汇编语言程序设计

ObjectKaz Lv4

进制与编码

数字逻辑

存储和寄存器

存储和内存布局

存储单位

单位长度二进制位
字节(基本单位)1 字节8
2 字节16
双字4 字节32
四字8 字节64
16 字节128
256 字节2048

地址

  1. 概念:为了标识和存取每一个存储单元,给每个存储单元规定一个编号,这个编号就是地址
  2. 内容:一个存储单元中存放的信息称为该存储单元的内容
  3. 低字节存入低地址,高字节存入高地址

小端方式:低对低、高对高的存储模式

  1. 每一个字节单元有一个唯一的存储地址
  2. 数据从高地址到低地址读取
  3. 数组数据按照顺序从低地址到高地址存储
  4. 多字节数据用低地址表示

例子

高字节低字节低字节地址
0004H0004H
12H12H34H34H0002H0002H

对于0002H0002H 单元:

  • 若按照字节来读取,则其值为34H34H
  • 若按照字来读取,则其值为1234H1234H

例子

对于 数据 'AB'

若按照字节存储(数组):

1
MESSAGE DB 'AB'

其为数组类型,A存入低字节,B存入高字节。

其存储格式为:

高字节低字节
42H42H41H41H

若按照字存储:

1
MESSAGE DW 'AB'

其不为数组类型,则认为它是一个整体(同普通数字的存储),A存入高字节,B存入低字节。

其存储格式为:

高字节低字节
41H41H42H42H

地址分段

  1. 解决的问题:在16位字长机器提供20位地址。

  2. 分段方法:段地址+段内偏移

  3. 段地址:

    1. 起始地址为 16 的倍数
    2. 每段最大长度为64KB64KB
    3. 1M寻址空间中,可分为166416-64 个逻辑段
  4. 逻辑地址(32 位):用两个字表示,书写格式为 段地址:偏移地址 ,存储格式为:

段地址(一个字)偏移地址(一个字)
  1. 物理地址(20 位):段地址×16d+段内偏移段地址 \times 16d + 段内偏移

例子

若一个逻辑地址为3015:002A3015:002A,则其物理地址为30150H+002AH=3017AH30150H + 002AH = 3017AH


地址对齐

  1. 原因:

    • 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的,某些平台只支持指定位数的地址。比如某 16 位平台只能按照字来读取数据
    • 性能原因:数据结构(如结构体)应该尽可能地在自然边界上对齐。为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
  2. 对齐方式:

    • 字单元安排在偶地址
    • 双字单元安排在模 4 地址
  3. 优点:

    • 简化了内存和 CPU 之间接口的设计。
    • 减少了额外的寻址开销和数据处理开销。

例子

对于 16 位机器,它一次性读取的数据为 2 个字节。在某些平台上,它只能按照字来读取数据。

若放入奇地址,则为了读取这个数据,它必须分成两次来读取,而且得到数据还有额外的计算开销:

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


寄存器

  1. 介绍:一个寄存器相当于运算器中的一个存储单元,其存储速度远大于主存。
  2. 功能:存放操作数地址、操作数和运算的中间结果。

数据寄存器

名称英文全称32 位版本16 位版本8 位版本功能
AAccumulatorEAXAXAH,AL累加器
BBaseEBXBXBH,BL基址变址
CCountECXCXCH,CL计数器
DDataEDXDXDH,DL通用寄存器

指针寄存器

名称英文全称32 位版本16 位版本8 位版本功能
SPStack PointerESPSP没有堆栈指针
BPBase PointerEBPBP没有基址指针

变址寄存器

名称英文全称32 位版本16 位版本8 位版本功能
DIDestination IndexEDIDI没有目的变址
SISource IndexESISI没有源变址

控制寄存器

名称英文全称32 位版本16 位版本8 位版本功能
IPInstruction Pointer没有IP没有指令寄存器,存放下一条命令的地址
FLAGS没有没有FLAGS没有标志

常用标志:

标志英文全称解释
OFOverflow Flag溢出标志
SFSign Flag符号标志
ZFZero Flag零标志
CFCarry Flag进位标志
AFAuxiliary Flag辅助进位标志
PFParity Flag奇偶标志
DFDirection Flag方向标志
TFTrap Flag陷阱标志
IFInterrupt Flag中断标志
IOPLI/O Privilege LevelI/O 特权级

段寄存器

名称英文全称32 位版本16 位版本8 位版本功能
CSCode Segment没有CS没有代码段,存放正在运行的程序
DSData Segment没有DS没有数据段,存放当前程序使用的数据
ESExtra Segment没有ES没有附加段,辅助的数据区
SSStack 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.不能用 AXCXDX 存放地址
2.可用寄存器:BXBPDISI
3.一般用于数组、字符串和表格处理
相对寻址(直接变址)某个寄存器所给的位置和指令中指定的位移量之和MOV AX,COUNT[SI],其中 COUNT[SI] 是相对寻址,地址值为 COUNTSI 之和1.不能用 AXCXDX 存放地址
2.可用寄存器:BXBPDISI
3.一般用于数组下标访问
基址变址基址寄存器和变址寄存器的内容之和MOV AX,[BX][SI],其中 [BX][SI] 是基址变址,地址值为 COUNTSI 之和1.基址寄存器:16 位只能使用 BXBP,32 位可以使用任意 32 位通用寄存器
2.变址寄存器:16 位只能使用 SIDI,32 位可以使用 ESP 外的任意寄存器
相对基址变址某个寄存器所给的位置、基址寄存器和变址寄存器的内容之和MOV AX,COUNT[BX][SI],其中 COUNT[BX][SI] 是相对基址变址,地址值为 COUNTSIBX 之和1.基址寄存器:16 位只能使用 BXBP,32 位可以使用任意 32 位通用寄存器
2.变址寄存器:16 位只能使用 SIDI,32 位可以使用 ESP 外的任意寄存器

转移寻址

寻址方式段地址(存入CSCS)偏移地址(存入IPIP)示例注意
段内直接寻址不变操作数的偏移地址JMP NEAR PTR ERROR短跳转:位移量为 8 位,符号前加 SHORT
近跳转:位移量为 16 位,符号前加 NEAR PTR
段内间接寻址不变操作数的值JMP WORD PTR [ERROR]
段间直接寻址操作数的段地址操作数的偏移地址JMP FAR PTR ERROR
段间间接寻址操作数的高字操作数的低字JMP DWORD PTR [ERROR]ERROR 是一个双字数据

程序格式

汇编源程序格式

概述

  1. 编写一个汇编源程序,需要做以下几件事:
    • 编写数据段
    • 编写代码段,指定代码入口
    • 使用 ASSUME 指定各段与寄存器的关系
    • 将各段装入相应的寄存器
    • 编写程序主代码
    • 结束程序
    • 汇编结束,并指定程序执行的起点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
; 数据段定义
DATAS SEGMENT
TESTDATA DB 1,2,3,4
TESTDATA1 DB 5,6,7,8
DATAS ENDS

CODES SEGMENT
ASSUME CS:CODES,DS:DATAS
START: ; 程序开始
MOV AX,DATAS ; 装填寄存器
MOV DS,AX

; 程序代码

MOV AH,4CH ; 程序退出
INT 21H
CODES ENDS ; 代码段结束
END START ; 汇编结束,并指定程序执行的起点

数据分段

  1. 代码段:
    • 概念:代码段用来存放程序的指令序列
    • 段寄存器:CSCS
    • 寻址:CS:IPCS:IP
  2. 堆栈段:
    • 概念:堆栈段确定堆栈所在的主存区域
    • 段寄存器:SSSS
    • 寻址:SS:SPSS:SP
  3. 数据段:
    • 概念:数据段存放当前运行程序所用的数据
    • 段寄存器:DSDS
    • 寻址:DS:EADS:EAEAEA 为数据的偏移地址
  4. 附加段:
    • 概念:附加的数据段,也用于数据的保存
    • 段寄存器:ESES
    • 寻址:ES:EAES:EAEAEA 为数据的偏移地址

表达式

表达式可以作为各指令的操作数。其值可以在汇编的时候确定。

  1. 标号/变量:存储的是地址,拥有段值、偏移量、类型三种属性

    • 标号、子程序名的类型可以是 NEAR(近)和 FAR(远),分别表示段内或段间
    • 变量名的类型可以是 BYTE(字节)、WORD(字)和 DWORD(双字)等
  2. 表达式:数字表达式 地址表达式

  3. 算术操作符: + 、- 、*、/、mod

  4. 逻辑和移位运算符:AND、OR、XOR、NOT、SHL(左移位)、SHR(右移位)

  5. 关系运算符:EQ(相等)、NE(不相等)、LT(小于)、LE(小于等于)、GT(大于)、GE(大于等于)

    • 真的结果为 0FFFFH
    • 假的结果为 0000H
  6. 数值回送运算符:

运算符说明
SEG expression分离出其后变量或标号所在段的段地址
OFFSET expression分离出其后变量或标号所在段的偏移地址
TYPE expression如果是变量,将返回该变量的类型对应字节数;
如果是标号,则返回代表标号类型的数值(NEAR=-1,FAR=-2)。
LENGTH expression取出变量所含的数据存储单元个数。
对于 DUP 定义的变量,返回重复的次数,否则返回 1
SIZE expression等于 LENGTH 变量 * TYPE 变量

示例

1
2
3
4
5
6
7
8
9
10
DATA	SEGMENT
A DB ‘ABCDEF’, 5 DUP( 0 )
B DW 10 DUP(1,2 DUP( 2 ) )
C DB 3,20 DUP(0)
D DB 3 DUP( 5 ), 0,10 DUP( ? )
DATA ENDS
MOV AX,LENGTH A ; AX=1
MOV BX,LENGTH B ; BX=10
MOV CX,LENGTH C ; CX=1
MOV DX,LENGTH D ; DX=3

  1. 属性操作符
    • PTR:type PTR expression 用于指定表达式的类型,type 有 byteword
    • 段操作符:段名:表达式 用于指定一个变量或标号的段地址
    • SHORT:用于指定转移地址的属性
    • THIS:类似 PTR ,可以建立指定类型或指定距离的地址操作数,其段地址和偏移地址和下一个存储地址相同
    • HIGH、LOW、HIGHWORD、LOWWORD:用于分离高字节、低字节、高字、低字

类型

  1. 数据类型
    • 汇编的数据类型只按照其大小进行区分,不区分有无符号、字符和整数
数据类型大小LENGTH 变量
BYTE1
WORD2
DWORD3
FWORD4
QWORD8
TWORD10
  1. 标号和子程序的类型
    • 标号 标号:,表示一行代码的起始地址
    • 标号和子程序的类型只区分要跳转的代码距离当前正在执行的代码的远近
数据类型大小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
2
3
段名	segment	定位  组合  段字  '类别'
... ;语句序列
段名 ends
  1. 定位:指定逻辑段在主存储器中的边界
说明
PARA(默认) 表示本段必须从能被 16 整除的地址处开始存放,即段起始地址最低四位必须是 0
WORD表示本段要从一个偶数地址处开始存放,即段起始地址的最低一位必须是 0
BYTE表示本段起始地址可以从任一地址处开始存放
PAGE表示本段要从能被 256 整除的地址处开始存放,即起始地址的最低八位必须是 0
  1. 组合:指定多个逻辑段之间的关系
说明注意
PRIVATE(默认) 本段与其他段没有逻辑关系,不与其他段合并,每段都有自己的段地址
PUBLIC连接程序把本段与所有同名同类型的其他段相邻地连接在一起,然后为所有这些段指定一个共同的段地址,也就是合成一个物理段
STACK本段是堆栈的一部分,连接程序将所有 STACK 段按照与 PUBLIC 段的同样方式进行合并。堆栈段必须具有该段组合
COMMON把该段和与该段名称相同的其他段重叠在一起形成一个重叠段(多个逻辑段拥有相同的段基值),重叠段的长度取决于其中最长逻辑段的长度。类似 C 的共用体。
MEMORY表示该段的起始地址位于其他所有段的后面(在程序中位于最高内存地址一端)。只有第一个被解释的 MEMORY 段作为 MEMORY 组合类型,后面的 MEMORY 段全部被解释为 COMMON 段
AT 表达式使用这种类型可以自己定义段的起始地址表达式的取值只能是 16 位二进制数,且低四位必须为 0
  1. 类别:

    • 当连接程序组织段时,将所有的同类别段相邻分配
    • 段类别可以是任意名称,但必须位于单引号中
  2. ASSUME 伪指令:指定各段和寄存器的关系,建立段寄存器与段的缺省关系。

    • 汇编程序会根据数据所在的逻辑段,在需要时自动插入段超越前缀。
    • ASSUME 伪指令并不为段寄存器设定初值,仅指定对应关系
    • ASSUME 伪指令可以重复使用,影响声明后的代码。
1
ASSUME register_name:segment_name,...

例子

假设有 ASSUME 伪指令:

1
ASSUME DS:DATAS

则它告诉编译器,在以后使用 DATAS 段中定义的符号时,默认使用 DS 寄存器作为段地址。


  1. 段寄存器的装填:在程序开始执行时,需要对寄存器进行装填。
    • CS 由系统自动装填
    • DS、ES、SS(未使用 STACK 作为组合类型的堆栈段)则由用户自行装填。
1
2
MOV AX,逻辑段名
MOV 寄存器名,AX
  1. 段组伪指令:把多个同类段合并为一个 64KB 物理段
    • 定义段组后,段组内各段就统一为一个段地址,各段定义的变量和标号的偏移地址就相对于段组基地址计算。
    • offset 操作符取变量和标号相对于段组的偏移地址,如果没有段组则取得相对于段的偏移地址。
    • 每个段的偏移地址是按照段组中段顺序来的
    • offset 后可以跟段组中的某个段名,表示该段最后一个字节后面字节相对于段组的偏移地址。
1
group_name GROUP segment1,segment2,...

例子

1
2
3
4
5
6
7
8
9
data1  segment  word
const1 dw 100
data1 ends
 
data2 segment word
var1 dw ?
data2 ends

datagroup group data1,data2

ASSUME DS:datagroup
则:

  • MOV BX,offset data1 时,BX=2BX=2
  • MOV BX,offset const1 时,BX=0BX=0
  • MOV BX,offset data2 时,BX=4BX=4
  • MOV BX,offset var1 时,BX=2BX=2

程序的开始

  1. NAME 作为模块名称
  2. TITLE 作为列表文件每一页上打印的标题,最大长度为 60 个字符
  3. 两种定义的关系:
    • 如果没有指定 NAME,则text 前六个字符作为模块名
    • 如果没有指定 TITLE,则使用文件名作为模块名
1
2
NAME module_name
TITLE text
  1. .STARTUP 伪操作:产生程序开始执行的代码
    • 按照 CPU 类型、存储模式、操作系统和堆栈类型,产生程序开始执行的代码
    • 同时还指定程序开始执行的起始点
    • 使用了 .STARTUP 则源程序结束时不需要指定程序运行起点

程序的结束

结束汇编(源程序结束)

  1. 格式:
    • [LABEL] 用于指定程序运行的起点
1
END [LABEL]

结束运行

  1. 通过 RET 进行结束
1
2
3
4
5
6
7
8
9
10
11
12
code  segment
main proc far
assume ……
start:
push ds
mov ax, 0
push ax
……
ret ; CS=DS,IP=0,此时会执行20H 中断指令
main endp
code ends
end start
  1. 通过中断进行结束
1
2
3
4
5
6
7
8
9
10
code  segment
assume ……
start:
……
……
mov ax,4c00h
int 21h
code ends
end start

  1. 产生结束的代码:可选参数是一个返回的数码,通常用 0 表示没有错误
    • 此伪指令会自动产生结束运行的代码
1
.EXIT [返回参数]

数据定义

1
[ 变量名 ]   大小标识  操作数项 [ ,操作数项,…  … ]
  1. 大小标识
大小标识大小
DB1B
DW2B(字)
DF3B
DD4B(双字)
DQ8B(四字)
DT10B
  1. 操作数项
类型解释示例
数字为一个或连续的存储单元设置数值初值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存入高字节。

其存储格式为:

高字节低字节
42H42H41H41H

若按照字存储:

1
MESSAGE DW 'AB'

其不为数组类型,则认为它是一个整体(同普通数字的存储),A存入高字节,B存入低字节。

其存储格式为:

高字节低字节
41H41H42H42H

  1. 多行变量的赋值
    • 前一行尾不可加逗号
    • 后续行不可丢伪操作符

例子

1
2
3
array	db	1,2,3,4,5
db 6,7,8,9
db 0ah,0bh

  1. LABEL 伪操作:定义别名
    • label 操作后面跟的数据定义即为其别名
    • 等价操作:EQU THIS
1
2
标识符 label 大小标识
标识符 EQU THIS 大小标识

例子

1
2
my_array label dw
array db 01H,02H,03H,04H

arraymy_array 代表了同一片存储空间,但 array 是字节,my_array 是字。


表达式赋值

EQU 伪操作

1
expression_name EQU expression
  1. 先定义后使用,且不能重复定义
  2. 表达式可以为常数、数值表达式、地址表达式、变量名、标号、寄存器名称、指令助记符等
  3. 源程序中的所有符号名都会被替换为它代表的内容,类似 C 语言的宏常量

例子

1
2
a	equ	var+4
mov ax,a

mov ax,a 汇编时变成 mov ax,var+4


= 伪操作

1
符号名  =  表达式
  1. 等号语句的作用和等值语句完全一致,区别是已经用等号定义过的符号可以再次使用等号修改其定义。
  2. EQU 伪操作和等号伪操作不能同时使用

地址计数与地址对齐

地址计数器 $

  1. 保存当前正在汇编的指令的偏移地址
  2. 同一个数据定义中,$ 的值不变

例子

1
2
3
4
DATAS SEGMENT
A_VAR DB 1
B_VAR DW $,$+1,$+2
DATAS ENDS

B_VAR 开始的 3 个 DW 值为 1,2,3


ORG 对齐

  1. 使紧跟其后的单元偏移地址为常数表达式计算出的值
  2. ORG 会修改地址计数器的值
  3. 特殊别名:
    • EVEN:使下一地址从偶地址开始
    • ALIGN boundary:从 boundary 的整数倍地址开始

例子

1
2
3
4
5
DATAS SEGMENT
A_VAR DB 1
ORG 20H
B_VAR DW $,$+1,$+2
DATAS ENDS

A_VAR 的偏移地址为 0B_VAR的偏移地址为 20HB_VAR开始的 3 个 DW 值为 20H,21H,22H


基数控制

1
.RADIX EXPRESSION
  1. 规定源程序中无标记数的基数( 2、8、10、16 选一)

宏汇编和重复汇编

宏汇编

宏定义和宏调用

  1. 宏定义
    • params1,params2,… 是哑元,可省
1
2
3
macro_name MACRO params1,params2,...
;code
ENDM
  1. 宏调用
    • params1,params2,… 是实元
1
macro_name params1,params2,...
  1. 宏必须先定义后调用

哑元和实元

  1. 哑元:相当于函数的形参
  2. 实元:相当于函数的实参
  3. 参数和操作码的连接:&

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
; 宏定义
JUMP_MACRO MACRO CONDITION,FUNC
J&CONDITION FUNC
ENDM

; 宏调用
MOV AX,0
SUB AX, 0
JUMP_MACRO Z,TEST_FUNC
TEST_FUNC:
; some code

; 展开结果
MOV AX,0
SUB AX, 0
JZ TEST_FUNC
TEST_FUNC:

  1. 在宏调用中,% 用于将表达式的值转换成当前基数下的数,而不是直接把表达式代入哑元

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
; 宏定义
TEST_MACRO MACRO NUM
MOV AX, NUM
ENDM

; 宏调用
CNTR = 0
TEST_MACRO % CNTR
CNTR = CNTR + 1
TEST_MACRO CNTR
CNTR = CNTR + 1
TEST_MACRO % CNTR
CNTR = CNTR + 1

; 宏展开结果
CNTR = 0
MOV AX, 0
CNTR = CNTR + 1
MOV AX, CNTR
CNTR = CNTR + 1
MOV AX, 2
CNTR = CNTR + 1

宏定义中的标号

1
LOCAL SIGN1,SIGN2,...
  1. LOCAL 操作符:宏展开时,LOCAL 操作符中出现的标号会变成全局唯一的符号。从而避免宏展开时,重名标号导致的报错。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
LOCAL_MACRO MACRO CONDITION,FUNC
LOCAL MAR
MAR:
MOV AX, 0
ENDM

; 宏调用
LOCAL_MACRO
LOCAL_MACRO
LOCAL_MACRO

; 宏展开结果
??0000:
MOV AX, 0
??0001:
MOV AX, 0
??0002:
MOV AX, 0

宏定义中的注释

  1. 指示在 .lst 中,宏如何展开的伪操作:
伪操作说明
.XALL(默认) 只展开代码,不展开单双分号注释
.SALL不列出任何展开信息
.LALL只展开代码,不展开双分号注释
  1. 双分号注释: ;; 在任何时候都不展开
  2. 单分号注释 :;.XALL 模式下展开

宏库

  1. 概念:把常用的宏建立成独立的文件
  2. 定义宏库:以 .mac.inc 结尾
  3. 使用宏库: INCLUDE 路径
    • 必须放在宏调用之前

删除宏

  1. PURGE 指令:
1
PURGE MACRO1,MACRO2,...

重复汇编

REPT

  1. 语法
1
2
3
REPT expression
; CODE
ENDM
  1. expression 的值将作为重复次数

IRP

  1. 用于把重复的代码按照参数进行重复。
  2. 重复时,每个参数都会赋值给 dummy(哑元,同宏里面的哑元)
1
2
3
IRP dummy,<argument1,argument2,argument3,...>
; CODE
ENDM

示例

1
2
3
4
5
6
7
8
9
10
11
12
; 重复定义
IRP X,<1,2,3,4,5,6>
MOV AX, X
ENDM

; 展开
MOV AX, 1
MOV AX, 2
MOV AX, 3
MOV AX, 4
MOV AX, 5
MOV AX, 6

IRPC

  1. 用于把重复的代码按照参数进行重复。
  2. 参数是一个字符串,重复时,会按照字符赋值给dummy(哑元,同宏里面的哑元)
1
2
3
IRPC dummy,string
; CODE
ENDM

示例

1
2
3
4
5
6
7
8
9
10
; 重复定义
IRPC K,ABCD
MOV K&X, 0
ENDM

; 展开
MOV AX, 0
MOV BX, 0
MOV CX, 0
MOV DX, 0

常用运算

传送

通用传送

(数据传送:MOV)

格式

1
MOV DST,SRC

注意

  • 不能同时为存储器
  • 不能同时为段寄存器
  • DST 不能存放立即数
  • DST 不能为 CS
  • 两个操作数大小必须一致

相当于 C 语言的赋值

1
2
int a;
int b = a;

(地址传送:LEA)

格式

1
LEA DST,SRC

注意

  • 不能使用立即数
  • 不能使用段寄存器
  • 若地址长度不一致,会进行低位截取零位拓展 操作。

相当于 C 语言的取地址后赋值

1
2
int a;
int *p = &a;

(数据交换:XCHG)
格式

1
XCHG DST,SRC

注意

  • 不能使用立即数
  • 不能使用段寄存器
  • 不能同时为存储器
  • CS 不能作为操作数

标志传送

LAHF:把 FLAGS 的低字节送入 AH
SAHF:把 AH送入 FLAGS的低字节
PUSHF:标志进栈
POPF:标志出栈

向其他硬件传送和获取数据

在里,每一个 I/O 设备都有固定的端口,CPU 和 I/O 设备的通信都是通过端口来完成的。

注意:NUM只能使用 ALAX 传送数据。

指令说明
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
2
MOV AX, H
PUSH AX


(POP:出栈)
格式

1
POP DST

注意:

  • 不能使用立即数
  • 操作数不能为 CSIP

出栈时,SP 会向高地址偏移,偏移量与操作数相同


示例

1
POP AX


算术运算

加减法

指令

指令操作进位(CF)置位条件溢出(OF)置位条件说明
ADD DST, SRCDST=DST+SRCDST=DST+SRC和的最高有效位有向高位的进位两个操作数符号相同,而结果符号与之相反加法
ADC DST, SRCDST=DST+SRC+CFDST=DST+SRC+CF和的最高有效位有向高位的进位两个操作数符号相同,而结果符号与之相反带进位加法
INC DST$DST=DST+和的最高有效位有向高位的进位两个操作数符号相同,而结果符号与之相反
SUB DST, SRCDST=DSTSRCDST=DST-SRC被减数的最高有效位有向高位的借位
即无符号数 DST < SRC
两个操作数符号相反,而结果的符号与减数相同减法
SBB DST, SRCDST=DSTSRCCFDST=DST-SRC-CF被减数的最高有效位有向高位的借位
即无符号数 DST < SRC + 两个操作数符号相反,而结果的符号与减数相同
带借位减法
DEC DST$DST=DST-被减数的最高有效位有向高位的借位两个操作数符号相反,而结果的符号与减数相同
NEG DSTDST=DSTDST=-DST操作数不为操作数为 -(字节运算)或操作数为 -(字运算)求补

理解

加法中,结果进位和溢出的判断
负数+正数:不会溢出
正数+负数:不会溢出
正数+正数:理论上结果为正才是正确,若出现了负数,就是溢出
负数+负数:理论上结果为负才是正确,若出现了正数,就是溢出

减法中,结果进位和溢出的判断

负数-正数:理论上结果为负数时才是正确,若出现了正数,就是溢出
正数-负数:理论上结果为正数时才是正确,若出现了负数,就是溢出
正数-正数:不会溢出
负数-负数:不会溢出


乘法

指令

指令操作说明
MUL SRC字节操作数AX=ALSRCAX=AL * SRC
字操作数DX,AX=AXSRCDX,AX=AX * SRC
无符号乘法
IMUL SRC字节操作数AX=ALSRCAX=AL * SRC
字操作数DX,AX=AXSRCDX,AX=AX * SRC
有符号乘法

标志位

标志位无符号乘法有符号乘法
进位 CF乘积的高一半为字为 DX,字节为 AH)时,其值为乘积的高一半是低一半的符号拓展(正数为负数为时,其值为
溢出 OF乘积的高一半为字为 DX,字节为 AH)时,其值为乘积的高一半是低一半的符号拓展(正数为负数为时,其值为
符号 SF两个数按照有符号数运算,结果为负数两个数按照有符号数运算,结果为负数
零 ZF结果为结果为

除法

指令

指令操作说明
DIV SRC字节操作数(AL,AH余数)=AX/SRC(AL 商,AH 余数)=AX/SRC
字操作数(AX,DX余数)=(DX,AX)/SRC(AX 商,DX 余数)=(DX,AX)/SRC
无符号除法
IDIV SRC字节操作数(AL,AH余数)=AX/SRC(AL 商,AH 余数)=AX/SRC
字操作数(AX,DX余数)=(DX,AX)/SRC(AX 商,DX 余数)=(DX,AX)/SRC
有符号除法

标志位:除法对任何标志位没有定义

位运算

常见逻辑运算

运算

指令操作功能说明
AND DST,SRCDST=DST & SRC屏蔽位
TEST DST,SRCDST & SRC测试某些位是否为测试,不保存结果
OR DST,SRCDST=DST | SRC保持某些位
NOT DSTDST=~DST将某数的各位按位取反取反
XOR DST,SRCDST=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. 字符输入
1
2
MOV		AH,1
INT 21H

从键盘输入字符的 ASCII 码送入寄存器 AL 中,并送显示器显示。

  1. 字符输出
1
2
3
MOV  DL,待显示字符的ASCII码
MOV AH,2
INT 21H

将 DL 寄存器中的字符送显示器显示。

字符串输入和输出

  1. 字符串输入
1
2
3
LEA	DX,缓冲区首偏移地址
MOV AH,10
INT 21H

缓冲区格式

第一个字节:缓冲区大小

第二个字节:实际输入字符数

第三个字节以后:缓冲区内容

1
BUFFER DB 128,0,128 DUP(0)
  1. 字符串输出
1
2
3
LEA	DX,待显示字符串首偏移地址
MOV AH,9
INT 21H

条件判断和跳转

条件判断

指令说明
CMP OPR1,OPR1执行 OPR1-OPR2,只置符号位,不保存结果

跳转指令

无条件跳转指令

指令说明
JMP DST无条件跳转到某个标号
寻址方式段地址(存入CSCS)偏移地址(存入IPIP)示例注意
段内直接寻址不变操作数的偏移地址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 DST
JE DST
ZF=1结果为 0 则跳转
JZ DST
JNE DST
ZF=0结果不为 0 则跳转

S 正负判断

指令跳转条件说明
JS DSTSF=1结果为负则跳转
JNS DSTSF=0结果非负则跳转

O 溢出判断

指令跳转条件说明
JO DSTOF=1溢出则跳转
JNO DSTOF=0未溢出则跳转

P 偶位判断

运算结果中,1 的个数为偶数个时,偶位为 1

指令跳转条件说明
JP DSTPF=1偶位为 1 则跳转
JNP DSTPF=0偶位为 0 则跳转

进位判断和无符号数的比较

进位指令 Carry小于指令 Below大于指令 Above跳转条件说明
JC DSTJB DSTJNAE DSTCF=1进位为 1 则跳转,或者两个无符号数比较DST < SRC
JNC DSTJNB DSTJAE DSTCF=0进位为 0 则跳转,或者两个无符号数比较DST >= SRC
JBE DSTJNAE DSTCF|ZF=1两个无符号数比较DST <= SRC
JNBE DSTJA DSTCF|ZF=0两个无符号数比较DST > SRC

有符号数的比较

小于指令 Lower大于指令 Greater跳转条件说明
JL DSTJNGE DSTSF^OF=1两个有符号数比较DST < SRC
JNL DSTJGE DSTSF^OF=0两个有符号数比较DST >= SRC
JLE DSTJNG DST(SF^OF)|ZF=1两个有符号数比较DST <= SRC
JNLE DSTJG DST(SF^OF)|ZF=0两个有符号数比较DST > SRC

理解 有符号数的小于比较

  1. 正数和正数比较:
    正数和正数相减,不会出现溢出。但是若结果为负数,则为 小于 关系。

  2. 负数和负数比较

    • 负数和负数相减,不会出现溢出。但是若结果为负数,则为 小于 关系。
  3. 正数和负数比较

    • 正数和负数相减,可能会出现溢出,且结果为负数。但这为 大于 关系。
  4. 负数和正数比较

    • 负数和正数相减,可能会出现溢出,且结果为正数。但这为 小于 关系。

循环

循环指令

指令循环条件说明
LOOP DSTCX != 0先进行CX=CX-1,再判断 CX 是否为 0,不为 0 则继续循环,否则退出循环
LOOPZ DSTCX != 0 && ZF == 0CX 不为 0,且 ZF 为 0 时进行循环,否则退出
LOOPNZ DSTCX != 0 && ZF != 0CX 不为 0,且 ZF 不为 0 时进行循环,否则退出

子程序

调用和返回指令

指令说明
CALL DST调用某个标号
RET [EXP]返回,EXP 可指定额外弹出的字数
  1. 近调用的过程

    • PUSH IP
    • MOV IP,IP+偏移量(直接近调用)

    MOV IP,目标地址(间接近调用)

  2. 近返回的过程

  • POP IP
  1. 远调用的过程

    • PUSH CS
    • PUSH IP
    • MOV CS,目标的段地址
    • MOV IP,目标的偏移地址
  2. 远返回的过程

    • POP IP
    • POP CS
  3. 带立即数 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 进行许可。