《汇编语言》Notes

知识来源:同济大学汇编教学课件

2023.9.5@Seymour0314

前言:重点跳转

第一课 基础知识

1.1 信息单位与数据类型

image-20230903202323153

1.2 进制转换

  • 进制
    • 十六进制 Hexadecimal,简写H
    • 十进制 Decimal,简写D
    • 八进制 Octonary,简写Q
    • 二进制 Binary,简写B

(1)X进制 转 十进制

  • 位权法
image-20230903201906663

(2)十进制 转换 X进制

  • 整数部分:除X取余,逆序排列
  • 小数部分:乘X取整,顺序排列
image-20230903202121187 image-20230903202104467

1.3 编码

  • 编码
    • 数值类型编码
      • 无符号数
        • 本身就是数值
      • 补码(有符号数)
        • 正数:最高位0,本身就是数值
        • 负数:最高位1,取反加1得到的是其绝对值。
    • 非数值类型编码
      • ASCII码
      • Unicode编码

1.4 溢出

  • 人为理解:

    • 超出表示范围即是溢出。
  • 汇编体现:

    • 无符号数:CF位置1,溢出。
    • 有符号数(补码):OF位置1,溢出。
  • 最值:

    • 无符号数:
    位数 最小 对应十进制 最大 对应十进制
    8bit 0000 0000 B 0 D 1111 1111 B 255 D
    16bit 0000 0000 0000 0000 B 0 D 1111 1111 1111 1111 B 65535D
    • 有符号数(补码):
    位数 最小 对应十进制 最大 对应十进制
    8bit 1000 0000 B -128 D 0111 1111 B +127 D
    16bit 1000 0000 0000 0000 B -32768 D 0111 1111 1111 1111 B +32767 D
    • 补码范围
      • 1B(8bit):-128 ~ +127
      • 2B(16bit):-32768 ~ +32767
      • 4B(32bit):-2147483648 ~ +2147483647
      • 8B(64bit):-2^32^ ~ 2^32^-1
  • 特点

    • 对于二进制数,保持位数不变计算(8bit计算后仍然取8bit),

      那么,取反加一减一取反 效果一样。

    • 正数x求补,得到对应的负数-x;

      负数-x求补,得到对应的正数x。

      求补是取反加一。

    • 无符号数 和 补码数,使用相同的加减法运算规则,

      其中补码的符号位参与运算,结果正确。

    • 无符号数 和 补码数,使用不同的溢出判断方法。

  • 分析溢出

    • 无符号数
      • 相加时,两数的最高位的数都是 1,则会溢出;
      • 相减时,被减数更小,会溢出。
    • 有符号数
      • 相加时,若两个数是相同符号位,计算结果的符号位却变化了,则是溢出;
      • 相减时,若两个数是不同符号位,计算结果的符号位与被减数不同,则是溢出。

1.5 字符表示

  • 采用非数值类编码的一种——ASCII码

  • 8bit信息,其中7bit编码,最高位0。

    能表示出128个字符。

  • 常用ASCII码

    字符 十进制 十六进制 字符 十进制 十六进制
    32 D 20 H CR回车 13 D 0D H
    0 48 D 30 H LF换行 10 D 0A H
    A 65 D 41 H Tab 9 D 09 H
    a 97 D 61 H BS退格 8 D 08 H

1.6 基本逻辑运算

image-20230903205019789

1.7 BCD码

  • 压缩BCD码

    每一位用4位二进制表示,一个字节表示两位十进制数

  • 非压缩BCD码

    用1个字节表示一位十进制数,高四位总是0000,低4位的0000到1001表示0到9

第二课 x86微机系统的组成

2.1 x86寄存器组

  • 寄存器

    用以临时存放指令执行所产生的中间结果存储单元

  • x86基本寄存器

image-20230903205223350

2.2 通用寄存器(数据寄存器)

  • 用于算术运算、逻辑运算和数据的传送

  • 虽然是通用寄存器,但各自又有专用用途

    (1) A。

    作为累加器使用,是算术运算所使用的主要寄存器。8位、16位和32位累加器分别对应AL, AX和EAX。

    (2) B。

    基址寄存器, 16位和32位基址寄存器分别对应BX,EBX。

    (3) C。

    计数器。8位、16位和32位计数器分别对应CL,CX和ECX。

    (4) D。

    数据寄存器。 8位、16位和32位数据寄存器分别对应DL, DX和EDX。

image-20230903205320087

2.3 指针寄存器

  • 指针寄存器(Pointer Register):ESP和EBP

  • 指针寄存器

    • SP/ESP

      堆栈指针。

      专门用以访问堆栈上数据的寄存器。32位模式下使用ESP,16位模式下使用SP,其内容始终指向堆栈栈顶。从这一点上看,ESP/SP是专用的。

    • BP/EBP

      基址指针。

      可以用来存放数据,但更经常、更重要的用途是作为堆栈区的一个基地址,以便访问堆栈中的数据。

2.4 变址寄存器

  • 变址寄存器(Index Register)包括ESI和EDI,其对应的16位寄存器分别是SI和DI。

  • 变址寄存器

    • SI/ESI

      源变址寄存器,对应的16位寄存器是SI, 32位的是ESI。

      可以存放数据,也可作为指针,存放内存单元地址。

    • DI/EDI

      目的变址寄存器,对应的16位寄存器是DI, 32位的是EDI。

      可以存放数据,也可作为指针,存放内存单元地址。

2.5 控制寄存器

  • 控制寄存器(Control Register)包括EIP和EFLAGS

  • 控制寄存器

    • IP/EIP

      用于指示待执行指令的位置。

      • 32位模式下是EIP,16位模式下是IP。
      • 运行期间,CPU自动修改(E)IP,程序不直接修改(E)IP,随着指令的执行,(E)IP相应地变动。
    • FLAGS/EFLAGS

      一条指令执行后,CPU所处的状态

      • 16位模式下是FLAGS
      • 主要有:运算结果标志、状态控制标志和系统状态标志等寄存器。
      image-20230903205830268 image-20230903205956709

记住:

  • Overflow Flag(OF):位序11,补码溢出。
  • Carry Flag(CF):位序0,进位、借位(无符号数溢出)。
  • Sign Flag(SF):位序7,符号位。
  • Zero Flag(ZF):位序6,零位。

2.6 段寄存器

  • 一个连续单元的内存区域为段,段寄存器是用于存放指示该段首地址的相关内容
  • 16位CPU有四个段寄存器:CS, DS, SS和ES,32位CPU又增加了两个:FS和GS
    • CS(Code Segment),指示代码段
    • DS(Data Segment),指示数据段
    • SS(Stack Segment),指示堆栈段
    • ES(Extra Segment), FS, GS,指示附加段

2.7 内存储器

  • 用途:存放执行的指令、所处理的数据

  • 基本单位:字节(Byte)(8bit)

  • 地址:每个字节单元的一个唯一编号

    • 寻址空间受限于地址总线宽度
    • 20-bit地址总线,可访问00000h~FFFFFh,共2^20^字节(1MB)
    • 32-bit,可访问00000000h~FFFFFFFFh,共2^32^字节(4GB)
    • 指令访问地址范围受字长限制

    注意区分 寻址编号、字节、比特。

    这里说的寻址空间到FFFFFH,那就是2^20^的单位,而寻址空间的单位是B,所以说是2^20^字节。

    也就是说,这里算寻址空间,你不要多此一举的算1B=8bit!

  • 内存单元与数据存放格式

    存储单元:连续若干个字节存储空间

    类型:

    • 字节单元
    • 2字节单元,又叫做字(Word)单元
    • 4字节单元,又叫双字(DWord)单元
    • 8字节单元,又叫四字(QWord)单元

    标识:以内存块的起始地址来标识

    存放格式:低位存入低单元,高位存入高单元(小端格式,Intel格式)

    image-20230903211135204
  • 示例

    image-20230903211215198

2.8 内存与分段

  • 内存的分段使用

    • 内存段:连续若干字节的内存区域
    • 基地址由段寄存器来确定
    • 地址形式为:段 : 偏移地址
    image-20230903211314155
  • 矛盾

    • 20-bit地址线:寻址空间:00000h~FFFFFh

    • 16-bit字长能表示范围:0000h ~ FFFFh

    • 解决办法:

      16位段地址和16位偏移地址,合成20位物理地址

      规则: 20位地址=段地址×16+偏移地址

      CPU 可以用不同的段地址和偏移地址形成同一个物理地址

    • 示例:

      段地址:1000h,偏移地址:1234h

      image-20230903211613512

2.9 栈及操作

  • 由连续若干个内存单元组成的、按FILO原则进行存取的数据结构

    • 栈底 堆栈中最先存入数据的单元栈顶
    • 堆栈中最后存入数据的单元
    • 进栈(PUSH) 将数据存入堆栈
    • 出栈(POP) 从堆栈取出数据
  • 栈底固定不变,栈顶随进栈和出栈操作而变化,由堆栈指针(Stack Pointer, SP) 指示

  • x86堆栈

    • 基地址:寄存器SS专用于确定堆栈区的基地址
    • 栈顶:(E)SP专门用于指示栈顶的位置,即总是指向最近进栈的数据位置。
    • 栈底:栈底固定为栈区的最高地址单元。执行PUSH操作后,(E)SP向低地址方向移动,执行POP操作后,(E)SP要向高地址方向移动
    • 栈单元基本单位是字(2字节)
      • 16位CPU,只能以2字节为单位进行进栈和出栈操作
      • 32位CPU,存取单位默认是4字节,但也可以是2字节
  • 说明

    • 栈操作主要指令是PUSH和POP指令
    • 与(E)SP密切相关,不宜将(E)SP用作数据寄存器

第三课 寻址方式及指令

3.1 指令系统概述

  • 机器指令:

    用二进制编码表示的指令,由操作码和操作数两部分组成

  • 汇编指令:

    用指令助记符和符号地址表示的指令

image-20230903212249659

3.2 操作数寻址方式

  • 三种操作数

    • 立即操作数,对应立即寻址方式
    • 寄存器操作数,对应的是寄存器寻址方式
    • 内存操作数,对应的内存操作数寻址方式
  • 寻址方式

    • 立即寻址 如1A2Bh

    • 寄存器寻址 如AX

    • 内存操作数寻址

      • 直接寻址 如[59A8h]

      • 寄存器间接寻址 如[BX]

      • 寄存器相对寻址 如4[BX]

      • 基址变址寻址 如 [BX][SI]

        • 基址:BX和BP,32-bit通用寄存器
        • 变址:SI和DI,32-bit通用寄存器(ESP除外)

        注意这里,不能交换位置!!!

      • 相对基址变址寻址 如100[BX][DI]

      以上内存操作数寻址方式,都是默认使用段寄存器DS。

      但是以BP/EBP寻址,默认用段寄存器SS。

3.3 MOV指令

  • 格式:MOV 目的操作数,源操作数

  • 功能:源操作数传送到目的储存单元(reg,seg,mem)

  • 操作数的寻址方式:

    image-20230903213407946

    重点:

    • imm不能做目的操作数
    • CS不能用作目的操作数
    • 类型必须一致且明确

    seg是段地址,mem是具体内存地址,不一样!

  • 说明

    • 一般情况下,指令源、目的操作数类型要一致
    • 立即数要在目的操作数类型值范围内
    • 类型均不明确时,须用Ptr指定操作数类型
      • Byte Ptr 指定字节类型(8位数据)
      • Word Ptr 指定2字节(字)类型(16位数据)
      • DWord Ptr 指定4字节(双字)类型(32位数据)
      • FWord Ptr 指定6字节类型(48位数据)
      • QWord Ptr 指定8字节(四字)类型(64位数据)
      • TByte Ptr 指定10字节类型(80位数据)
  • 示例

image-20230903213530216

3.4 XCHG指令

  • 格式:XCHG 操作数1,操作数2

  • 功能:数据交换,操作数1与操作数2单元的内容互相交换

    由于两操作数即是源操作数,又是目的操作数,故它们的位置顺序无关紧要

  • 操作数的寻址方式:

    image-20230903213731391

    重点:

    • 不能有imm
    • 源操作数必须是reg
    • 类型必须一致且明确
  • 示例

image-20230903213822325

3.5 PUSH指令

  • 格式:PUSH 源操作数

  • 功能:数据进栈

    • 源操作数为16位,(E)SP内容减2
    • 源操作数为32位,(E)SP中内容减4

    注意,栈操作单位只能是16/32位。8086CPU只能是16位

    即,不可 PUSH AL,只能PUSH AX。

  • 操作数的寻址方式:

image-20230903214216809
  • 示例
image-20230903214257432

3.6 POP指令

  • 格式:POP 目的操作数

  • 功能:从栈中弹出数据到目的操作数所确定的单元中

    • 操作数为16位,(E)SP向高地址端调整2字节
    • 操作数为32位,(E)SP向高地址端调整4字节

    注意,栈操作单位只能是16/32位。8086CPU只能是16位

    即,不可 POP AL,只能POP AX。

  • 操作数寻址方式:

image-20230903214456667
  • 示例
image-20230903214518927
  • 使用实例
1
2
3
4
5
使用PUSH和POP指令,将字(16位)单元[1000h]内容和字单元[2000h]内容分别放入EAX低16位和高16位。
解:先将两个16位数按存放顺序要求进栈,再从栈弹出一个32位数到EAX
PUSH Word Ptr[2000h]
PUSH Word Ptr[1000h]
POP EAX
image-20230903214743932

3.7 XLAT指令

  • 格式:XLAT

  • 功能:查表换码。

    将以(E)BX基址,以AL内容为位移量的字节单元内容传送给AL。

    这条指令使用隐含操作数。在指令执行前约定:必须已经建立一个字节表,表首地址已经放入基址寄存器(E)BX;查找项的位移量已经放入AL

  • 实例

image-20230903214910464

3.8 地址传送指令LEA

  • 格式:LEA 寄存器,源内存操作数
  • 功能:有效地址送寄存器。将内存操作数的偏移地址(EA)传送至目的寄存器中。
  • 操作数的寻址方式:
image-20230903215004437
  • 若是16位寄存器则只装入EA的低16位

  • 实例

1
2
3
4
5
(1) LEA	AX, [EBX][ESI]
(2) MOV AX, [EBX][ESI]
解:
指令(1)取源操作数的EA,并存入AX,
指令(2)取EA所对应的字字单元内容AX。

3.9 地址传送指令2

  • 格式:LDS/LSS/LES/LFS/LGS 目的寄存器,源内存操作数

  • 功能:

    将源内存操作数中的低2/4字节内容 存入 目的寄存器

    高2字节内容 存入 段寄存器DS/SS/ES/FS/GS

  • 操作数的寻址方式:

image-20230903215343729
  • 示例
image-20230903215437424

3.10 标志位传送指令

1
2
3
4
5
6
7
8
9
10
11
12
1)  LAHF指令
格式:LAHF
功能:将FLAGS的低8位送至AH
2)  SAHF指令
格式:SAHF。
功能:将AH内容送至标志寄存器低8位
3)  PUSHF/PUSHFD指令
格式:PUSHF/PUSHFD。
功能:16/32位标志寄存器FLAGS/EFLAGS内容进栈
4)  POPF/POPFD指令
格式:POPF/POPFD。
功能:从栈弹出16/32位数据FLAGS/EFLAGS

3.11 扩展指令

1
2
3
4
5
6
7
8
9
10
11
CBW
字节扩展到字,符号位填充,不影响真值
AL扩展,AH填充,存在AX

CWD
字扩展到双字,符号位填充,不影响真值
AX扩展,DX填充,存在DX:AX

CWDE
字扩展到双字,符号位填充,不影响真值
AX扩展,EAX填充,存在EAX

3.12 输入输出指令

  • IN、OUT指令专门用来读写I/O端口
  • 只能使用累加器(AL/AX/EAX)来接收、发送数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1)  IN指令
格式:IN 累加器,端口地址。
功能:1/2/4字节端口数据AL/AX/EAX
端口地址范围:0000h~FFFFh。
imm8形式,指令中的端口范围:00h~FFh
DX存放端口,指令中的端口:0000h~FFFFh
和内存操作数不同的是,IN指令的源操作数据是来自I/O端口

2)  OUT指令
格式:OUT 端口地址,累加器。
功能:AL/AX/EAX内容1/2/4字节端口
imm8形式,指令中的端口范围:00h~FFh
DX存放端口,指令中的端口:0000h~FFFFh
和内存操作数不同的是,OUT指令的目的操作数据将传送到I/O端口中去
  • 示例
image-20230903215802562

3.13 DOS系统功能调用

  • 介绍
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(1) 01h。输入一个字符,有回显。
入口:AH=01h
出口:AL=输入字符的ASCII码

(2) 02h。输出一个字符。
入口:AH=02h
DL=待输出字符的ASCII码
出口:无

(3) 08h。输入一个字符,无回显。
入口:AH=08h
出口:AL=输入字符的ASCII码

(4) 09h。输出一个字符串。
入口:AH=09h
DS:DX=字符串地址(以'$'作为结束标志)
出口:无

(5) 0Ah。读入字符串(以回车结束,有回显)
入口:AH=0Ah
DS:DX=输入缓冲区地址(字节0允许字符数)
出口:输入缓冲区字节1:字符数,字节2:字符串

(6) 0Bh。检查是否有字符可读。
入口:AH=0Bh
出口:AL=00h,无;FFh,有

(7) 4Ch。终止程序的执行,返回DOS。
入口:AH=4Ch
AL=返回的代码
出口:无

3.14 汇编使用

  • 前置操作
1
2
3
4
MOUNT C: D:\MASM
PATH=%PATH%;D:\Work
C:
DIR
  • 以编写的001.ASM为例介绍
1
2
3
4
5
TYPE 001.ASM	查看类型信息
MASM 001.ASM 汇编
LINK 001.OBJ 连接
DEBUG 001.EXE 调试
001.EXE 运行
image-20230903233053074

汇编ASM文件的文件名字不能过长,否则无法打开。

  • DEBUG操作
1
2
3
4
5
6
7
8
查看和修改寄存器内容的命令 R
显示内存单元内容的命令 D
输入汇编指令的命令 A
反汇编命令 U
执行命令 G
追踪(单步执行)命令 T
执行命令 P
退出 DEBUG 命令 Q

第四课 源程序结构、运算指令

4.1 源程序结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
_STACK    SEGMENT  STACK  'STACK'
DB 1000H DUP(?)
TOS DW ?
_STACK ENDS

_DATA SEGMENT
a DW 10H
b DW 20H
x DW ?
_DATA ENDS

_TEXT SEGMENT
ASSUME CS: _TEXT, DS: _DATA
START:
MOV AX, _DATA ; 设置数据段、堆栈 start
MOV DS, AX
CLI
MOV AX, _STACK
MOV SS, AX
MOV SP, OFFSET TOS
STI ; 设置数据段、堆栈 end

; 比较ab大小
MOV AX, a
CMP AX, b ; 注意这里仍然是a
JG loc1
MOV AX, b

loc1:
MOV x, AX

MOV AX, 4C00H ; 退出
INT 21H
_TEXT ENDS
END START
image-20230903233146848

4.2 常数、变量和标号

  • 相关内容

    • 与数据相关的常量与变量
    • 与转移地址相关的标号与过程
  • 标识符

    标号名、变量名、过程名、段名等称为标识符

    • 组成标识符字符:

      1
      字母(A~Z, a~z)、数字(0~9)及?、.、@、$和_(下画线)
    • 数字不能作首字符,'.'只能作为标识符的首字符。

    • 标识符的长度没有严格限制,一般不应超过31字符

    • 保留字不能作为标识符使用。

(1)常数

  • 常量是数的本身,不对应任何存储单元(寄存器或内存单元),

    其数值在汇编期间已能够完全确定,且在程序运行期间也不会发生变化。

  • 常量一般可分为:

    • 数值常数

      • 整数常数

        各种进制均可,如: 255D,0B8h,1100B,144Q

        为区别于标识符,字母开头的十六进制数前须加0

        通常常数默认为十进制,此时后面D或d可省略

        1
        2
        可以用伪指令RADIX改变默认的数制,其格式为:
        .RADIX 用十进制形式表示的基数
      • 实数常数

        实数其实就是带小数点的数,有两种形式表示:

        (1)带小数点的十进制数形式,例如: -1.414

        (2)指数形式,例如: 4.56E-2, -1.732E+10

        实常数以浮点格式存放。

    • 字符串常数

      字符串常数是用‘(单撇号)"(双撇号)括起来的单个字符或多个字符

      (1)对于ASCII字符来说,其数值是字符对应的ASCII码的值

      • 'd’对应的是64h
      • 'AB’对应的是41h, 42h

      (2)对于汉字来说,其数值是汉字的内码

      • ‘你好!’,对应是:0C4h, 0E3h 0Bah, 0C3h, 21h
  • 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(1) 作为立即数出现在指令中
MOV AX, 1234h
SUB AL, '0'
AND EAX, 7FFFFFFFh

(2) 作为内存操作数的位移量(地址编号)
MOV AX, [1000h]
ADD 100h[BX], AL
TEST EAX, [1000h+EBX+EDX*4]

(3) 为数据定义预置初始值
Variable DW 1234h
Str DB "Hello, World!", 13, 10, '$'
x DQ 4.756E3

(2)变量

  • 变量是存放数据的内存单元名称

    • 变量名是符号地址
    • 具有类型属性:字节、字、双字…
  • 定义格式:

    1
    2
    3
    4
    [变量名]  数据定义伪指令  初值表  [;注释] 

    DB、DW、DD、DF、DQ、DT
    1B、2B、4B、6B、8B、10B类型
    • 分配一个或多个指定类型的内存单元,并可用变量表示该内存单元

    • 如果有变量名,那么它仅代表所定义的数据存储区的第一个单元地址

  • 定义的形式——初值表

    1. 常数或常数表达式

    2. “?”形式:内容不确定。一般情况下,程序在汇编时以0填充。

    3. 符号地址及地址表达式:符号地址对应的地址编号,是无符号整型常数。

    4. 可用DUP把某项重复n次:其格式为 n DUP(数据项)

  • 属性

    • 地址属性

      • 段地址可由运算符SEG返回

      • 偏移地址可由运算符OFFSET返回

      1
      2
      MOV AX,SEG Msg	;将Msg的段地址送到AX
      MOV BX,OFFSET Msg ;将Msg的偏移地址送到BX

      注:当需要存取某一变量时,必须先将该段的段地址放到相应的段寄存器(如DS、ES等)

      1
      2
      MOV	AX, _DATA
      MOV DS, AX
    • 类型属性

      伪指令DB, DW, DD, DF, DQ, DT可定义1, 2, 4, 6, 8, 10字节类型

      可以指定数据类型

      1
      2
      3
      4
      5
      6
      Byte Ptr	指定数据或变量为字节类型(8位)
      Word Ptr 指定数据或变量为2字节(字)类型(16位)
      DWord Ptr 指定数据或变量为4字节(双字)类型(32位)
      FWord Ptr 指定数据或变量为6字节类型(48位)
      QWord Ptr 指定数据或变量为8字节(四字)类型(64位)
      TByte Ptr 指定数据或变量为10字节类型(80位)

      变量名对应的是内存单元地址,是无符号符号整常数,

      加、减一个整常数就是地址编号加、减一个整常数,

      仍然对应一个内存单元地址,其类型与原变量相同。

      1
      2
      3
      L	DD	1234A1B2h, 87654321h
      L是4字节类型单元,L+1对应的也是4字节类型单元,其内容是211234A1h
      L+1可以写成L[1],注意,L[1]是L的偏移地址加1而不是加1×4

      每种数据类型的类型值就是其占用的字节数,可用运算符TYPE将它分离出来

  • 示例1

1
2
3
4
5
6
7
8
9
10
Msg		DB	 	"Hello"     	;定义变量,分配5个1B空间,置初值
DB 13, 10, '$' ;分配3个B空间,置初值
Cnt DW 5*20, -2 ;定义变量,分配2个2B空间,置初值
S1 DB ?, ?, ?, ? ;定义变量,分配4个1B空间,未置初值
DD 123456h,? ;分配2个4B空间

取’H‘:
MOV AL, Msg
取’e’:
MOV AL, Msg+1
  • 示例2
1
2
3
4
5
Msg		DB		"Hello"
DB 13, 10, "$"
Cnt DW 5*20, -2
L DD 1234A1B2h, 87654321h
F DQ 1.5
image-20230903234937787
  • 示例3
1
2
3
Msg	DB	"Hello" 13, 10, "$"
p1 DW Msg, Msg+5
p2 DD Msg, Msg+5
image-20230903235205880
  • 示例4
1
s1	DB	"你好", 2 DUP('!'), 2 DUP ('A', 'B'), 3 DUP(1, 2, 2 DUP('$'))
image-20230903235425341
  • 示例5
1
2
3
4
5
6
Variable	DW	100 DUP(0)
用变量名表示的内存操作数寻址为
直接寻址: MOV AX, Variable
寄存器相对寻址: MOV AX, Variable [SI]
相对基址变址寻址: MOV AX, Variable [BX][DI]
比例因子寻址: MOV EAX, DWord Ptr Variable [EBX][4*ECX]

(3)标号

  • 定义

    • 标号表示的是指令在内存中存放的位置

    • 标号定义的格式是:

    1
    标号名:

    表示标号后首条指令在内存中地址。

    标号既可以定义在目的指令同一行的最前面,

    也可以在目的指令前一行单独用一行定义。

  • 属性

    • 地址属性

      标号代表的是其后首条指令在内存中地址

      • SEG来返回标号所在段的段地址
      • OFFSET来返回标号所在段的偏移地址
      1
      2
      MOV AX,SEG loc1			;将loc1的段地址送到AX
      MOV BX,OFFSET loc1 ;将loc1的偏移地址送到BX
    • 类型属性

      标号具有NEAR和FAR两种属性,其类型值分别为-1和-2

      可用运算符TYPE返回其类型值

(4)变量名和标号的其他定义方式

  • 用LABEL和EQU来定义变量名或标号

    • 标识符 LABEL 类型
    • 标识符 EQU THIS 类型
  • 功能:

    • 定义一个指定类型的变量名或标号类型为BYTE, WORD, DWORD, FWORD, QWORD和TBYTE等,则定义变量名

    • 类型是NEAR和FAR,则定义标号

  • 只是将当前地址定义为一个变量名或标号并不为它们分配内存空间

  • 示例1

1
2
3
4
5
6
7
8
bVariable	LABEL	BYTE
wVariable DW 100
第一条伪指令仅仅定义了一个具有字节类型的变量名,但并没有为它分配存储空间;
第二条伪指令定义了一个字类型的变量,而且为它分配了2字节的存储空间。
符号地址bVariable与wVariable代表的是同一个地址,但具有不同的数据类型属性。

Loc LABEL FAR
定义了一个具有远转移类型的标号,该标号指向其后第一条汇编指令的存放地址。
  • 示例2
1
2
3
4
5
6
7
8
wVariable	EQU THIS	DWORD
dwVariable DD 100
第一条伪指令仅仅定义了一个具有2字节类型的变量名,但并没有为它分配存储空间;
第二条伪指令定义了一个4字节类型的变量,而且为它分配了4字节的存储空间。
符号地址dwVariable与wVariable代表的是同一个地址,但具有不同的数据类型属性。

Loc EQU THIS NEAR
定义了一个具有近转移类型的标号,该标号指向其后第一条汇编指令的存放地址。

4.3 表达式和运算符

  • 将常数、符号地址及其符号常量用运算符连接起来的有意义的式子

  • 值的计算是在源程序汇编过程中完成的

  • 运算符分为:

    • 算术运算符
    image-20230904012401011
    • 逻辑运算符
    image-20230904012425615
    • 关系运算符
    image-20230904012444725
    • 数值返回运算符
    image-20230904012504628
    • 属性运算符
    image-20230904012522665
    • 字节分离运算符

4.4 MASM的基本伪指令

伪指令在汇编程序对源程序汇编期间由汇编程序处理的操作,它们可以完成诸如定义程序段、定义数据、分配存储区和指示程序结束等功能。伪指令在形式上与一般指令相似,但伪指令只是为汇编程序提供有关信息,不产生相应的机器代码。

指令集选择伪指令

  • MASM在默认情况下只接受8086指令集。

    如果程序员需要使用8086以后微处理器新增加的指令,必须使用指令集选择伪指令。

image-20230904012739548

完整的段定义伪指令

  • 使用完整的段定义伪指令来定义一个段,

    可具体控制汇编程序MASM和连接程序LINK在内存中组织代码和数据的方式

  • 格式

1
2
3
段名 SEGMENT [定位类型] [组合方式] [地址模式] ['分类名']

段名 ENDS
  • 功能

    在程序中定义一个逻辑段,指定段的名字和范围,段在内存中的起始位置,以及段与段之间的连接关系。

  • 段指定伪指令ASSUME

  • 段组定义伪指令GROUP

  • 源程序开始与结束伪指令

    • 源程序开始伪指令

    • 源程序结束伪指令END

    • 数据定义伪指令

    • 符号定义指令

      • 等值伪指令EQU
      • 等号伪指令=

      与EQU THIS不一样,

      EQU THIS是定义一个符号地址

      EQU是定义一个为常量、表达式及其他符号定义一个符号名

4.5 地址计数器与对准伪指令

  • 地址计数器$

    • 指示当前正在处理的汇编指令或伪指令所在处的偏移地址
    1
    2
    3
    4
    5
    6
    7
    8
    (1)当$用在指令中时,它表示该指令的第一个字节的地址。例如,
    JNE $+6
    转向地址是JNE指令的首地址加上6。

    (2)当$用在数据定义等伪指令的参数字段时,表示的是地址计数器的当前值。例如,
    ARRAY DW 1, 2, $, 3, 4, $
    假设汇编时ARRAY分配的偏移地址为0074,则汇编后的存储区内容如下:
    ARRAY→ 01 00 02 00 78 00 03 00 04 00 7E 00
  • 定位伪指令ORG

    • 将地址计数器设置到指定的位置

    用在数据段中:

    1
    2
    3
    4
    5
    6
    _DEMO	SEGMENT
    ORG 10
    V1 DW 47A5h
    ORG $+3
    V2 DW 5C96h
    _DEMO ENDS
    image-20230905164729590

    用在代码段中:

    1
    2
    3
    4
    		ORG		100h
    JMP Loc
    ORG 200h
    Loc: MOV AX, 1200h

    那么指令JMP Loc存放的地址为100h,

    同理指令MOV AX, 1200h存放的地址为200h,

    而100h~200h的存储空间中,除了JMP指令外,其余内容均为未初始化内容。

  • 其他伪指令

    • EVEN伪指令

      使下一个变量或指令开始于偶数地址处。

    • ALIGN伪指令

      使下一个变量或指令开始于2^n^整数倍地址处。

4.6 子程序定义PROC和ENDP

1
2
3
子程序名 PROC [NEAR/FAR] 
 … ;子程序体
子程序名 ENDP

子程序名是子程序的入口地址的符号表示,是符号地址,也具有地址属性和类型属性。

4.7 其他伪指令

  • 注释伪指令COMMENT

    1
    2
    格式:COMMENT 分隔符 注释内容 分隔符。
    功能:分隔符之间的任何内容均作为注释内容,但分隔符本身不能出现在注释内容中。
  • 文件包含伪指令INCLUDE

    1
    2
    格式:INCLUDE文件名。
    功能:在源程序中,将一个外部文件插入到此伪指令所在处。
  • 全局符号名说明伪指令PUBLIC

    1
    2
    3
    格式:PUBLIC符号名1[, 符号名2, …]。
    功能:将本模块中定义的一个或多个符号名说明为全局符号,这样在其他模块中可以引用该符号。
    - 符号名可以是变量、符号常量、标号或过程名。
  • 外部符号名说明伪指令EXTRN

    1
    2
    格式:EXTRN符号名1:类型[, 符号名2:类型, …]。
    功能:说明本模块中将要引用的外部模块中的符号名,类型:Byte, Word, DWord, FWord, QWord, TByte, Near, Far;常数则是ABS。

4.8 MASM的宏汇编伪指令

  • 宏指令

    1
    2
    3
    宏指令名 MACRO [形式参数1,形式参数2,…,形式参数n] 
    … ; 宏指令体(宏体)
    ENDM
    • 宏操作符

      连接运算符&文本操作符<>表达式操作符%字符操作符!

    • 宏库中的宏指令

      可在源程序中使用包含伪指令INCLUDE

  • 重复汇编

    固定重复伪指令REPT,不定重复伪指令IRP和单字符参数的不定重复伪指令IRPC,均以ENDM作为结束标志

  • 条件汇编

    根据某些条件是否成立(为真)来决定是否汇编某一段代码

  • 结构、联合和记录

    类似C语言的结构体、共用体复合数据类型的定义

  • 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
示例:定义了宏指令ExitToDos和InOutStr,并在在代码段中调用它们

ExitToDOS MACRO
MOV AX, 4C00h
INT 21h
ENDM
InOutStr MACRO Fun,str
MOV AH, Fun
LEA DX, str
INT 21h
ENDM

_STACK SEGMENT STACK
DB 32766 DUP (?)
TOS DW ?
_STACK ENDS

_DATA SEGMENT
Msg DB 'Hello, World!',13,10,'$'
_DATA ENDS

_TEXT SEGMENT 'CODE'
ASSUME CS:_TEXT,DS:_DATA,SS:_STACK
Start: MOV AX, _DATA
MOV DS, AX
CLI
MOV AX, _STACK
MOV SS, AX
MOV SP, Offset TOS
STI
InOutStr 9, Msg
ExitToDOS
_TEXT ENDS
END Start

4.9 算术运算指令

(1)加法

  1. ADD指令
    格式:ADD目的操作数,源操作数。
    功能:将源操作数与目的操作数相加,结果存入目的操作数。

    1
    2
    ADD reg, reg/mem/imm
    ADD mem, reg/imm

    根据结果置CF, AF, PF, ZF, SF, OF的状态

  2. ADC指令
    格式:ADC目的操作数,源操作数。
    功能:即将源操作数、目的操作数和CF相加,结果存入目的操作数。

    1
    2
    ADC reg, reg/mem/imm
    ADC mem, reg/imm

    根据结果置CF, AF, PF, ZF, SF, OF状态

  3. INC指令
    格式:INC 操作数。
    功能:操作数自身加1,即将操作数加1,结果再存入操作数。

    1
    INC reg/mem 

    影响AF, PF, ZF, SF和OF,不影响CF

(2)减法

  1. SUB指令

    格式:SUB 目的操作数,源操作数。
    功能:目的操作数减去源操作数,结果存入目的操作数。

    1
    2
    SUB reg, reg/mem/imm;
    SUB mem, reg/imm。

    根据结果置CF, AF, PF, ZF, SF, OF的状态

  2. SBB指令

    格式:SBB 目的操作数,源操作数。
    功能:目的操作数减去源操作数,再减去CF,结果存入目的操作数。

    1
    2
    SBB reg, reg/mem/imm;
    SBB mem, reg/imm。

    根据结果置CF, AF, PF, ZF, SF, OF的状态

  3. DEC指令

    格式:DEC 操作数。
    功能:操作数自身减1,即操作数减去1,结果再存入操作数。

    1
    DEC reg/mem

    影响AF, PF, ZF, SF和OF,不影响CF

(3)求补

  1. NEG指令

    格式:NEG 操作数。
    功能:操作数各位取反再加1(求补),即将0减去操作数,结果存入操作数。

    1
    NEG reg/mem

    影响CF, AF, PF, ZF, SF, OF。

    只有当操作数为0时,才使CF=0,其他情况则均为1;
    只有当操作数为-2^7^(8位运算)或-2^15^(16位运算)或-2^31^(32位运算)时,才使OF=1,其他情况则均为0。

(4)比较

  1. CMP指令

    格式:CMP 目的操作数,源操作数。
    功能:两操作数比较大小,根据目的操作数减去源操作数的运算结果,从而设置标志位。

    1
    2
    CMP reg, reg/mem/imm
    CMP mem, reg/imm

    该指令影响CF, AF, PF, ZF, SF, OF。
    这条指令除了相减结果不保存外,其他情况与SUB指令完全相同

(5)乘法

  • MUL无符号数乘法

  • IMUL有符号数乘法

格式:MUL/IMUL 源操作数
功能:无/有符号数乘指令

1
MUL/IMUL reg/mem
image-20230905171946359 image-20230905172010743

两者的区别在于:

MUL的操作数内容看作无符号数

IMUL操作数内容看作补码

影响CF和OF,不影响其他标志位

  • MUL,乘积的高半部分为0,则CF和OF均为0,否则CF和OF均为1
  • IMUL,乘积的高半部分是低半部分的符号扩展,则CF和OF均为0,否则均为1。

通过测试这两个标志位,就能够知道乘积的高半部分是否有效数字 。

(6)除法

  • DIV无符号数除法

  • IDIV有符号数除法

要求被除数的位数必须是除数的两倍

格式:DIV/IDIV源操作数
功能:无/有符号数除法指令

1
DIV/IDIV reg/mem
image-20230905172351909 image-20230905172400510

两者的区别在于:

DIV的操作数是无符号数,商和余数均为无符号数;

IDIV操作数是补码,商和余数均为有符号数,余数符号与被除数符号相同。

  • 可能导致两类错误:除数为零,商溢出。

  • 当除法运算所得的商超过表示范围时,就产生商溢出。

  • 除法指令对所有标志位无定义

4.10 逻辑运算指令

逻辑指令包括逻辑运算指令和移位指令

(1)逻辑运算指令

五个指令
  • 逻辑运算是按位操作的,包括:AND, OR, NOT, XOR和TEST指令。

  • AND指令

    1
    2
    AND reg, reg/mem/imm
    AND mem, reg/imm

    根据结果置SF, ZF和PF,CF=0,OF=0,AF无定义。

  • OR指令

    1
    2
    OR reg, reg/mem/imm
    OR mem, reg/imm

    根据结果置SF, ZF和PF,CF=0,OF=0,AF无定义

  • NOT指令

    1
    NOT reg/mem

    不影响标志位

  • XOR指令

    1
    2
    XOR reg, reg/mem/imm
    XOR mem, reg/imm

    根据结果设置SF, ZF和PF,CF=0,OF=0,AF无定义

  • TEST指令

    1
    2
    TEST reg, reg/mem/imm
    TEST mem, reg/imm

    根据结果置SF, ZF和PF,CF=0,OF=0,AF无定义

    TEST与AND都是两操作数按位“逻辑与”,但TEST的结果不保存

常用逻辑
  • 指定位清0

    AND需清0的位赋0,其他位赋1

  • 指定位置1

    OR需置1的位赋1,其他位赋0

  • 指定位变反

    XOR需变反的位赋1,其他位赋0

(2)移位运算指令

  • 按规定的方式,对目的操作数执行向左或向右移动若干个二进制位数的操作。

    包括:

    • 逻辑移位指令

      SHL、SHR

    • 算术移位指令(移位后正负号 不变 )

      SAL、SAR

    • 循环移位指令

      ROL、ROR、RCL、RCR

逻辑移位
  1. SHL指令
    格式:SHL 目的操作数,移动位数。
    功能:目的操作数逻辑左移,最后移出的位进入CF,最低位用0填充

    1
    SHL reg/mem, imm8/CL

    影响CF, OF, SF, ZF, PF,而AF不确定。

    OF在左移1位时有效,否则不确定。
    左移1位后,若符号位改变,OF=1,否则OF=0

    image-20230905173934473
  2. SHR指令

    格式:SHR目的操作数,移动位数。
    功能:目的操作数逻辑右移,最后移出的位进入CF,最高位用0填充

    1
    SHR reg/mem, imm8/CL。

    影响CF, OF, SF, ZF, PF,而AF不确定。

    OF在右移1位时有效,否则不确定。
    右移1位后符号位改变,OF=1,否则OF=0

    image-20230905173944234
算术移位

(移位后正负号 不变 )

  1. SAL指令

    格式:SAL目的操作数,移动位数。
    功能:算术左移指令。

    SAL与SHL是同一条指令,即一个操作码对应的两个助记符

  2. SAR指令

    格式:SAR 目的操作数,移动位数。
    功能:目的操作数算术右移,最后移出的位进入CF,高位用符号位填充

    1
    SAR reg/mem, imm8/CL

    影响CF, OF, SF, ZF, PF,而AF不确定。

    OF在右移1位时有效,否则不确定。
    右移1位后,OF=0

    image-20230905174152094
循环移位1
  1. ROL指令

    格式:ROL目的操作数,移动位数。
    功能:目的操作数循环左移,最后移出的位进CF

    1
    ROL reg/mem

    影响CF, OF, SF, ZF, PF,而AF不确定。

    OF在左移1位时有效,否则不确定。
    左移1位后,若符号位改变,OF=1,否则OF=0

    image-20230905174459195
  2. ROR指令

    格式:ROR 目的操作数,移动位数。
    功能:目的操作数循环右移,最后移出的位进CF

    1
    ROR reg/mem, imm8/CL

    影响CF, OF, SF, ZF, PF,而AF不确定。

    OF在右移1位时有效,否则不确定。
    右移1位后,若符号位改变,OF=1,否则OF=0

    image-20230905174745980
循环移位2
  1. RCL指令

    格式:RCL 目的操作数,移动位数。
    功能:目的操作数和CF一起循环左移

    1
    RCL reg/mem, imm8/CL

    影响CF, OF, SF, ZF, PF,而AF不确定。

    OF在左移1位时有效,否则不确定。
    左移1位后,若符号位改变,OF=1,否则OF=0

    image-20230905174905476
  2. RCR指令

    格式:RCR目的操作数,移动位数。
    功能:目的操作数和CF一起循环右移

    1
    RCR reg/mem, imm8/CL

    影响CF, OF, SF, ZF, PF,而AF不确定。

    OF在右移1位时有效,否则不确定。
    右移1位后,若符号位改变,OF=1,否则OF=0

    image-20230905174948402

第五课 转移、选择、循环

5.1 转移指令

  • CS:(E)IP指向下一条要执行的指令在内存中的地址,

    控制转移指令实际上通过改变CS:(E)IP来达到控制程序的执行流程。

    包括:

    • 无条件转移指令
    • 条件转移指令
    • 循环指令
    • 子程序调用和返回指令
    • 中断调用和中断返回指令

    JMP,JccCALL,RETINT,IRET

  • 转移

    • 仅能改变(E)IP,是近转移或段内转移
    • 能改变(E)IPCS,是远转移或段间转移

(1)无条件转移

  • 格式:

    1
    JMP	目标地址
  • 功能:无条件转移到目标地址,执行从该地址开始的指令序列

1
2
3
4
5
6
7
8
9
	MOV		AX, A
CMP AX, B
JNA loc2
MOV X, 1
JMP loc3
loc2:
MOV X, 2
loc3:
...

(2)条件转移

  • 条件

    • 条件满足时则转移,条件不满足时,则顺序执行后面的指令
    • 具体条件见下面的指令
      • 单个标志位的条件
      • 无符号数比较的条件
      • 有符号数比较的条件
  • 格式

    常用Jcc来代表这类指令的助记符

    1
    Jcc	标号

    条件转移指令都是段内直接近转移

单个标志位
  • Z、S、C、O、P均有各种的指令
image-20230905180521286

简记:

记住JEJNE为相等则跳转、不相等则跳转。

无符号数比较
  • 无符号数比较时,根据CF来判断大小
image-20230905180708147

简记:

  • A为无符号大
  • B为无符号小
  • 拼加N、E
有符号数比较
  • 有符号数比较时,根据SF和OF来确定大小
image-20230905181049991

简记:

  • G为有符号大 Greater
  • L为有符号小 Less
  • 拼加N、E

5.2 循环指令

  • 主要使用LOOP指令比较多

  • LOOP会以CX寄存器存着的值作为循环次数,进行循环跳转

    执行到LOOP LOC1

    会先执行CX - 1

    再判断:

    • CX不等于0:跳转到LOC1位置执行
    • CX等于0:循环结束,执行LOOP LOC1后面的语句
image-20230905181644577

5.3 其他指令

(1)标志位处理指令

image-20230905182143234

(2)其他指令

image-20230905182302120

第六课 子程序调用

6.1 子程序定义

  • 子程序定义

    1
    2
    3
    子程序名 PROC [NEAR/FAR] 
     … ;子程序体
    子程序名 ENDP

    子程序名是子程序的入口地址的符号表示,

    是符号地址,也具有地址属性和类型属性。

  • 当主调程序转向子程序时,使用调用指令,而在子程序执行结束时,安排一条返回指令,使子程序返回到主程序。

  • 为保证正确的返回,

    (1)调用子程序时,自动将下一条指令地址保存到栈中,

    (2)返回时根据栈中先前保存的地址,转移到主程序继续执行。

  • 子程序调用与返回指令是配套使用的

6.2 CALL指令

  • 格式:

    1
    CALL 目标地址
  • 功能:

    把该指令之后的地址进栈,再转移到目标地址,执行从该处指令。

    有直接调用和间接调用两种方式。

  • 直接调用的CALL指令后也可以是标号,

    但是要遇到RET才会返回,否则会无法退出。

6.3 RET指令

  • 格式:

    1
    RET/RET [imm16]
  • 功能:

    首先从栈栈顶弹出返回的目标地址,然后转移到该地址处执行。

    若其后有imm16,则还要执行

    image-20230905184126486

    与CALL指令配套使用,实现从子程序中返回,继续主程序的执行 。

6.4 寄存器的保护与恢复

  • 主程序和子程序通常是分别编制的,所以它们所使用的寄存器往往会发生冲突。

    • 进入子程序后,把子程序所需要使用的寄存器内容保存在栈中,此过程称作寄存器保护

    • 在退出子程序前把寄存器内容恢复原状,此过程称作寄存器恢复

  • 寄存器保护与现场恢复分别使用压栈和出栈指令实现

1
2
3
4
5
6
7
8
9
10
11
12
CRLF	PROC	Far	;子程序定义开始,属性类型为Far
PUSH AX ;AX寄存器内容的保护
PUSH DX ;DX寄存器内容的保护
MOV AH, 2
MOV DL, 13
INT 21h
MOV DL, 10
INT 21h
POP DX ;AX寄存器内容的恢复
POP AX ;DX寄存器内容的恢复
RET ;返回调用程序(远返回指令,由汇编程序确定)
CRLF ENDP ;子程序定义结束

根据需要,可用PUSHF和POPF来保护和恢复标志位。

6.5 参数传递

  • 参数传递

    主程序在调用子程序时,经常要向子程序传递一些参数或控制信息,子程序执行后,也常需要把运行的结果返回调用程序。这种信息传递称为参数传递。

  • 常用的方法有:

    • 约定寄存器传递参数
    • 约定内存单元传递参数
    • 栈传递参数

    经常同时并用。

6.6 静态变量与动态变量

  • 静态

    在**段(数据段)**中定义分配的数据是静态数据。

    通过DW等伪指令定义并分配存储空间,可用变量名,或直接地址访问相应的内存单元。

  • 动态

    上分配存储空间来临时使用是动态数据。

    • 原则:进入子程序,分配临时空间;子程序执行结束,释放临时空间
    • 方法:通过调整栈指针来分配空间
    • 访问:以 (E)BP为基址,用位移量来存取临时变量(因为动态分配,没有名字)
  • 示例

    编写程序计算(x % m + y % m + z % m) % m的值,其中x, y, z为32位无符号数,m是16位无符号数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    foo	PROC	FAR
    PUSH BP ;保护基址指针
    MOV BP,SP ;设置新栈帧
    SUB SP,6 ;分配临时空间
    PUSH DX
    MOV AX,[BP+16]
    MOV DX,[BP+18]
    DIV Word Ptr[BP+6]
    MOV [BP-2],DX ;保存余数
    MOV AX,[BP+12]
    MOV DX,[BP+14]
    DIV Word Ptr[BP+6]
    MOV [BP-4],DX ;保存余数
    MOV AX,[BP+8]
    MOV DX,[BP+10]
    DIV Word Ptr[BP+6]
    MOV [BP-6],DX ;保存余数
    SUB DX,DX
    MOV AX,[BP-2]
    ADD AX,[BP-4]
    ADC DX,0
    ADD AX, [BP-6]
    ADC DX, 0
    DIV Word Ptr[BP+6]
    MOV AX, DX
    POP DX
    ADD SP,6 ;释放临时空间
    MOV SP, BP
    POP BP
    RET
    foo ENDP

6.7 子程序的嵌套与递归调用

  • 一个子程序包含有子程序的调用,这便是子程序的嵌套调用。

  • 一个程序直接或间接地调用自身就是子程序的递归调用,它递归子程序。

  • 设计递归子程序

    • 每次调用的参数进栈
    • 在栈上分配动态空间保存中间结果
  • 示例

    编写求n!的递归子程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    f	PROC	NEAR
    PUSH BP ;保护基址指针
    MOV BP, SP ;设置新栈帧
    SUB SP, 2 ;分配临时空间
    MOV AX, [BP+4] ;取n
    MOV [BP-2], AX
    CMP AX, 1
    JE L10
    DEC AX
    PUSH AX
    CALL f ;调用递归子程序
    ADD SP, 2
    PUSH DX
    MUL Word Ptr [BP-2] ;n×(n-1)!
    POP DX
    L10: MOV SP, BP
    POP BP
    RET
    f ENDP
    image-20230905185118581

6.8 多模块程序设计

  • 一个汇编语言程序可以划分成主模块和多级、多个子模块。

  • 每个模块的源程序都是以END伪指令来结束,

    但是只有主模块中的END后可以接标号,以表示程序的入口地址,

    其他模块中的END后不能有标号。

全局符号与外部符号

  • PUBLIC伪指令与EXTRN伪指令配套使用。
  1. 全局符号名说明伪指令PUBLIC
    格式:

    1
    PUBLIC符号名1[, 符号名2, …]

    功能:将符号名说明为全局符号,以便其他模块引用
    符号名:变量、标号或子程序名,及表示常数的符号

  2. 外部符号名说明伪指令EXTRN
    格式:

    1
    EXTRN符号名1:类型[, 符号名2:类型, …]

    功能:说明符号名为外部符号。
    本模块引用其他模块中的符号,应说明为外部符号。
    类型是:Byte, Word, DWord, FWord, QWord, TByte, Near, Far, ABS,应与原模块定义的类型一致。

  • 由于符号地址都是在某段中定义,

    所以,必须将各模块中的段指定为PUBLIC属性,

    这样,连接程序将不同模块中的同名段放在一起,从而为各全局符号分配正确的偏移地址。

  • 示例

    • 模块M1.ASM实现的是输入字符串子程序Sub1;

    • 模块M2.ASM实现的是输出字符串子程序Sub2;

    • 主模块M0.ASM调用Sub1和Sub2,输入字符串后再将它显示出来。

    M0.ASM

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    ;	M0.ASM  主模块源程序
    PUBLIC Buf, DOSFUN
    EXTRN Sub1: Near, Sub2: Far
    DOSFUN = 21h
    _DATA SEGMENT 'DATA' USE16 PUBLIC
    CRLF DB 13, 10, '$'
    Buf DB 20, 20 DUP (32), 13, 10, '$'
    _DATA ENDS
    _TEXT SEGMENT 'CODE' USE16 PUBLIC
    ASSUME CS: _TEXT, DS: _DATA
    Start: …
    CALL Sub1
    MOV AX, Offset CRLF
    PUSH AX
    CALL Sub2
    POP AX
    MOV AX, Offset Buf+2
    PUSH AX
    CALL Sub2
    POP AX
    MOV AX, 4C00h
    INT DOSFUN
    _TEXT ENDS
    END Start

    M1.ASM

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    ;	M1.ASM  输入字符子程序源程序
    EXTRN DOSFUN: Abs
    EXTRN Buf: Byte
    PUBLIC Sub1
    _TEXT SEGMENT 'CODE' USE16 PUBLIC
    ASSUME CS: _TEXT
    ;子程序Sub1约定输入缓冲区为Buf
    Sub1 PROC NEAR
    LEA DX, Buf
    MOV AH, 0Ah
    INT DOSFUN
    SUB BX, BX
    MOV BL, Buf [1]
    MOV Buf[BX+2],20h
    RET
    Sub1 ENDP
    _TEXT ENDS
    END ;模块至此结束

    M2.ASM

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    ;	M2.ASM  显示字符子程序源程序。
    PUBLIC Sub2
    EXTRN DOSFUN: Abs
    _TEXT SEGMENT 'CODE' USE16 PUBLIC
    ASSUME CS: _TEXT
    ;子程序Sub2,约定栈传递参数
    Sub2 PROC FAR
    PUSH BP
    MOV BP, SP
    MOV DX, [BP+6]
    MOV AH, 9
    INT DOSFUN
    POP BP
    RET
    Sub2 ENDP
    _TEXT ENDS
    END ;模块至此结束

连接方法

image-20230905185801120

6.9 子程序库

  • 常用子程序写成独立的源文件,单独汇编形成OBJ文件后,放到一个专门的文件中,形成子程序库。
    • 文件类型一般为.LIB
    • 在连接时,调入子程序库中的子程序模块,生成最终的可执行文件
    • LIB.EXE,就专门用于建立和修改子程序库

6.10 其他

  • 与高级语言程序的连接

    各种语言的源程序分别编写,

    在各自的开发环境中编译成目标模块.OBJ,

    再将各目标模块连接在一起,生成可执行文件。

  • 遵守约定规则

    • 汇编模块必须使用高级语言的标识符命名约定。

    • 在汇编语言源程序中,如果使用定义在高级语言的符号,必须用EXTRN来说明它,如果让高级语言使用在本模块中定义的符号,则必须用PUBLIC来说明它;同理,在高级语言源程序中,也须用相应的说明语句来说明全局符号与外部符号。

    • 调用子程序时的参数传递规则主要有:PASCAL规则(参数自左向右压栈),C规则(参数自右向左压栈),以及返回值的传递规则。

    • 在子程序返回时的栈恢复:由调用程序恢复,还是由子程序恢复。

    • 其他诸如寄存器保护原则、数据类型的对应等。

  • C编译程序的约定

    • 全局符号名(变量名或函数名)前加“_”(下画线)。

    • 调用子程序时,使用C规则传递参数;在16位模式下,用AX传递8, 16位返回结果,用DX:AX传递32位返回结果,在32位模式下,用EAX传递8, 16和32返回结果,用EDX:EAX传递64位返回结果。

    • 由调用程序来恢复栈指针。

    • 一般情况下,不保护AX, BX, CX, DX(16位地址模式),或EAX, EBX, ECX, EDX(32位地址模式)。

      在VC中似乎要保护ebx; C++规则,要保护ecx

第七课 中断、输入/输出

7.1 中断

  • 中断调用

    • 标志寄存器进栈
    • 中断调用指令后地址的段和偏移地址进栈
    • 从中断向量表取中断服务程序入口地址,并转去执行
  • 中断向量

    中断服务子程序的入口地址称为中断向量。

    实模式,内存的最低1KB区域专门用来保存256个中断向量,称为中断向量表。

  • 实地址模式下的中断向量表

image-20230905190154631
  • INT指令

    格式:

    1
    INT imm8

    功能:

    产生一次类型号为imm8的中断:

    • 首先FLAGS进栈,再将IF和TF清0;

    • 然后该指令之后的地址(返回地址),按段、偏移的次序进栈;

    • 最后转向类型号为imm8的中断向量,即双字单元0000h:[4×imm8]所确定的中断服务子程序入口地址处执行。

    由于一条INT指令相当于产生一次中断,其处理过程与外部中断处理过程一样,所以,INT指令又称为软中断指令。

  • INTO指令

    格式:

    1
    INTO

    功能:

    若OF=1,产生一次类型号为4的中断,相当于INT 4;否则顺序执行。

  • IRET指令

    格式:

    1
    IRET

    功能:

    中断处理结束,返回中断发生处继续执行。

    即:首先从栈中以“偏移地址、段地址、16位标志”这样的次序弹出转向的目标地址和FLAGS,再转移到该目标地址去执行。

7.2 输入/输出

image-20230905190625072
  • 用IN、 OUT指令与外部设备交换信息

    端口

  • 与I/O设备间数据传送方式:

    • 程序直接控制I/O方式
    • 中断传送方式
    • 直接内存存取(DMA)

7.3 中断处理程序的主要步骤

1
2
3
4
5
6
7
(1) 保存寄存器的内容;
(2) 若允许CPU响应外设中断,则开中断(STI);
(3) 处理中断;
(4) 关中断(CLI);
(5) 若是I/O中断服务程序,则送中断结束命令(EOI)给中断命令寄存器;
(6) 恢复寄存器的内容;
(7) 返回被中断的程序(IRET)。
  • 中断服务程序的入口地址只有放到中断向量表中,才能在中断发生时被调用到。

    所以,除了中断处理程序,还有相应设置中断向量及相应的硬件初始化工作。

补录:实用示例

1. 打印可见的十六进制数

  • 题目

    16位二进制数存在BX,以可见字符形式输出打印在屏幕上

  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    PUBLIC  DISPBX
    CODE SEGMENT
    ASSUME CS:CODE
    DISPBX PROC FAR
    MOV CH,4
    MOV CL,4
    ROTATE: ROL BX,CL
    MOV AL,BL
    AND AL,0FH
    ADD AL,30H
    CMP AL,3AH
    JL PRINTIT
    ADD AL,7
    PRINTIT:MOV DL,AL
    MOV AH,2
    INT 21H
    DEC CH
    JNZ ROTATE
    RET
    DISPBX ENDP
    CODE ENDS
    END

2. 输出对应字符串

  • 题目

    编写多分支结构程序,实现:
    输入’1’, ‘2’, ‘3’,输出"one", “two”, “three”;
    输入’0’,中止运行;
    输入其他,输出"error!"

  • 代码

    方法一:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    _DATA	SEGMENT 'DATA'
    Msg DB 13,10,"Please input a number: ",13,10,"$"
    s1 DB 9, "one", 13, 10, '$'
    s2 DB 9, "two", 13, 10, '$'
    s3 DB 9, "three", 13, 10, '$'
    err DB 9, "error!", 13, 10, '$'
    _DATA ENDS
    _TEXT SEGMENT 'CODE'
    ASSUME CS: _TEXT, DS: _DATA
    Start: MOV AX, _DATA
    MOV DS, AX
    Again: MOV AH, 9
    MOV DX, Offset Msg
    INT 21h
    MOV AH, 1
    INT 21h
    CMP AL, '0'
    JE Case0
    CMP AL, '1'
    JE Case1
    CMP AL, '2'
    JE Case2
    CMP AL, '3'
    JE Case3
    Default: MOV DX, Offset err
    JMP EndSwitch
    Case0: MOV AX, 4C00h
    INT 21h
    Case1: MOV DX, Offset s1
    JMP EndSwitch
    Case2: MOV DX, Offset s2
    JMP EndSwitch
    Case3: MOV DX, Offset s3
    JMP EndSwitch
    EndSwitch: MOV AH, 9
    INT 21h
    JMP Again
    _TEXT ENDS
    END Start

    方法二:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    _DATA	SEGMENT 'DATA'
    Msg DB 13, 10, "Please input a number: ", 13, 10, "$"
    s1 DB 9, "four", 13, 10, '$'
    s2 DB 9, "five", 13, 10, '$'
    s3 DB 9, "six", 13, 10, '$'
    err DB 9, "error!", 13, 10, '$'
    _DATA ENDS
    _TEXT SEGMENT 'CODE'
    ASSUME CS: _TEXT, DS: _DATA
    Start: MOV AX, _DATA
    MOV DS, AX
    Again: MOV AH, 9
    MOV DX, Offset Msg
    INT 21h
    MOV AH, 1
    INT 21h
    SUB AL, '4'
    JL Error ;输入字符小于'4'的情况
    CMP AL, 3
    JLE Loc1
    Error: MOV AL, 4
    Loc1: MOV AH, 0
    ADD AX, AX
    MOV BX, AX
    JMP Word Ptr CS: CaseTab [BX]
    CaseTab DW Case1, Case2, Case3, Case4, Default
    Default: MOV DX, Offset err
    JMP EndSwitch
    Case1: MOV DX, Offset s1
    JMP EndSwitch
    Case2: MOV DX, Offset s2
    JMP EndSwitch
    Case3: MOV DX, Offset s3
    JMP EndSwitch
    Case4: MOV AX, 4C00h
    INT 21h
    EndSwitch: MOV AH, 9
    INT 21h
    JMP Again
    _TEXT ENDS
    END Start

    方法三:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    _DATA	SEGMENT 'DATA'
    Msg DB 13,10,"Please input a character: ",13,10,"$"
    s1 DB 9, "File", 13, 10, '$'
    s2 DB 9, "Edit", 13, 10, '$'
    s3 DB 9, "View", 13, 10, '$'
    err DB 9, "Error!", 13, 10, '$'
    _DATA ENDS
    _TEXT SEGMENT 'CODE'
    ASSUME CS: _TEXT, DS: _DATA
    Start: MOV AX, _DATA
    MOV DS, AX
    Again: MOV AH, 9
    MOV DX, Offset Msg
    INT 21h
    MOV AH, 1
    INT 21h
    MOV CX, CS: TabItems
    MOV BX, Offset CaseTab
    Next_I: CMP Byte Ptr CS: [BX], AL
    JE ToCase
    ADD BX, 4
    LOOP Next_I
    ToCase: JMP Word Ptr CS: [BX+2]
    TabItems DW 8
    CaseTab DW 'F',Case1,'E',Case2,'V',Case3,'X',Case4
    DW 'f',Case1,'e',Case2,'v',Case3,'x',Case4,0,Default
    Default: MOV DX, Offset err
    JMP EndSwitch
    Case1: MOV DX, Offset s1
    JMP EndSwitch
    Case2: MOV DX, Offset s2
    JMP EndSwitch
    Case3: MOV DX, Offset s3
    JMP EndSwitch
    Case4: MOV AX, 4C00h
    INT 21h
    EndSwitch: MOV AH, 9
    INT 21h
    JMP Again
    _TEXT ENDS
    END Start

3. 求所有n2数字和

  • 题目

    用while循环结构编写程序,
    计算s=12+22+32+…+n2+…,直到n>65000或s≥7FF0000h为止,
    运算结果存于32位变量Sum中

    image-20230905182834174
  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    _DATA	SEGMENT	'DATA'	
    Sum DD ?
    _DATA ENDS
    _TEXT SEGMENT 'CODE'
    ASSUME CS: _TEXT, DS: _DATA
    Start: MOV AX, _DATA
    MOV DS, AX
    MOV CX, 1
    MOV Word Ptr Sum, 0
    MOV Word Ptr Sum+2, 0
    JMP Rep1? ; 先执行条件测试
    Rep1b: MOV AX, CX
    MUL CX
    ADD Word Ptr Sum, AX
    ADC Word Ptr Sum+2, DX
    INC CX
    Rep1?: CMP CX, 65000
    JNBE EndRep1
    CMP Word Ptr Sum+2, 7FFh
    JB Rep1b
    EndRep1:
    MOV AX, 4C00h
    INT 21h
    _TEXT ENDS
    END Start

4. 计算字符数

  • 题目

    编一汇编语言程序,从键盘输入一系列字符(小于80个,以回车符结束),

    字母数字以及其他字符分类统计,并显示(可以按十六进制数显示)出这三类的计数结果。

  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    _DATA	SEGMENT
    N1 DB 0 ; 数字
    N2 DB 0 ; 字符
    N3 DB 0 ; 其他
    BUF DB 80 ;预定义80字节的空间
    DB ? ;待输入完成后,自动获得输入的字符个数
    DB 80 DUP(?)
    Msg1 DB 13,10,"Please input a string:","$"
    Msg2 DB 13,10,"Number:","$"
    Msg3 DB 13,10,"Char:","$"
    Msg4 DB 13,10,"Other:","$"
    _DATA ENDS
    _TEXT SEGMENT
    ASSUME CS: _TEXT, DS: _DATA
    Start:
    MOV AX, _DATA
    MOV DS, AX

    ; 输出提示信息
    MOV AH, 09H
    MOV DX, OFFSET Msg1
    INT 21H
    ;读入字符串
    MOV AH, 0AH
    MOV DX, OFFSET BUF
    INT 21H

    ; 核心部分,统计个数
    MOV CL, BUF[1]
    MOV CH, 00H
    MOV SI, 0
    ; 进行判断
    NEXT:
    CMP BUF[2][SI], '0'
    JL LOC1
    CMP BUF[2][SI], '9'
    JG LOC1
    INC N1
    JMP OVER
    LOC1:
    CMP BUF[2][SI], 'A'
    JL LOC2
    CMP BUF[2][SI],'Z'
    JG LOC2
    INC N2
    JMP OVER
    LOC2:
    CMP BUF[2][SI], 'a'
    JL LOC3
    CMP BUF[2][SI], 'z'
    JG LOC3
    INC N2
    JMP OVER
    LOC3:
    INC N3
    OVER:
    INC SI
    CMP SI, CX
    JLE NEXT

    ; 显示结果提示信息
    MOV AH, 09H
    MOV DX, OFFSET Msg2
    INT 21H
    ; 输出 数字 个数
    MOV AH, 02H
    MOV BL, N1
    ADD BL,30H
    MOV DL, BL
    INT 21H
    ; 显示结果提示信息
    MOV AH, 09H
    MOV DX, OFFSET Msg3
    INT 21H
    ; 输出 字母 个数
    MOV AH, 02H
    MOV BL, N2
    ADD BL,30H
    MOV DL, BL
    INT 21H
    ; 显示结果提示信息
    MOV AH, 09H
    MOV DX, OFFSET Msg4
    INT 21H
    ; 输出 其他 格式
    MOV AH, 02H
    MOV BL, N3
    ADD BL,30H
    MOV DL, BL
    INT 21H

    MOV AX,4C00H ; 退出
    INT 21H

    _TEXT ENDS
    END Start

5. 找出最大偶数

  • 题目

    编一汇编语言程序,找出首地址为DATA的N个数组中的最大偶数,并将该最大偶数按十六进制显示出来。

    (前面给出的DISPBX.ASM作为本题目的子模块,连接使用即可。)

  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    _DATA	SEGMENT  PUBLIC
    DATA DW 1234H,5678H,9D4CH,0D7H,0,-1,7D2AH,8A0EH,10F5H,645DH
    N EQU ($-DATA)/2
    MAX DW ?
    _DATA ENDS

    EXTRN DISPBX:FAR

    _TEXT SEGMENT PUBLIC
    ASSUME CS: _TEXT, DS: _DATA

    ; 主程序,求出最大偶数,存放在BX
    _MAIN PROC FAR
    PUSH DS
    MOV AX,0
    PUSH AX
    MOV AX, _DATA
    MOV DS, AX
    ; 主程序内容
    ; 初始化
    MOV CX, N-1
    MOV BX, OFFSET DATA -2
    INIT:
    INC BX
    INC BX
    TEST WORD PTR [BX], 0001H ; 只取最低1位判断是否为偶数
    JNZ INIT ; 不是偶数,则直接进行下一个判断
    MOV AX, [BX]
    MOV BX, OFFSET DATA
    ; 比较大小
    AGAIN:
    INC BX ; 注意这里要BX+2,自增两次
    INC BX
    TEST WORD PTR [BX], 0001H ; 只取最低1位判断是否为偶数
    JNZ NEXT ; 不是偶数,则直接进行下一个判断
    CMP AX, [BX]
    JGE NEXT
    MOV AX,[BX]
    NEXT:
    LOOP AGAIN
    MOV MAX, AX ; 结束循环,得到最大偶数,准备输出
    ; 调用子程序
    MOV BX, MAX
    CALL DISPBX
    ; 退出
    RETF
    _MAIN ENDP
    _TEXT ENDS
    END _MAIN