《汇编语言》Notes
《汇编语言》Notes
知识来源:同济大学汇编教学课件
2023.9.5@Seymour0314
前言:重点跳转
基本指令
3.3 MOV指令、PUSH指令、POP指令、LEA指令等
DOS功能调用
3.13 DOS系统功能调用
基础知识
4.2 常数、变量和标号
更多指令
4.9 算术运算指令
4.10 逻辑运算指令
5.1 转移指令
5.2 循环指令
模板使用
一、模板
第一课 基础知识
1.1 信息单位与数据类型
1.2 进制转换
- 进制
- 十六进制 Hexadecimal,简写H
- 十进制 Decimal,简写D
- 八进制 Octonary,简写Q
- 二进制 Binary,简写B
(1)X进制 转 十进制
- 位权法
(2)十进制 转换 X进制
- 整数部分:除X取余,逆序排列
- 小数部分:乘X取整,顺序排列
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 基本逻辑运算
1.7 BCD码
-
压缩BCD码
每一位用4位二进制表示,一个字节表示两位十进制数
-
非压缩BCD码
用1个字节表示一位十进制数,高四位总是0000,低4位的0000到1001表示0到9
第二课 x86微机系统的组成
2.1 x86寄存器组
-
寄存器
用以临时存放指令执行所产生的中间结果存储单元
-
x86基本寄存器
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。
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
- 主要有:运算结果标志、状态控制标志和系统状态标志等寄存器。
-
记住:
- 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格式)
-
示例
2.8 内存与分段
-
内存的分段使用
- 内存段:连续若干字节的内存区域
- 基地址由段寄存器来确定
- 地址形式为:段 : 偏移地址
-
矛盾
-
20-bit地址线:寻址空间:00000h~FFFFFh
-
16-bit字长能表示范围:0000h ~ FFFFh
-
解决办法:
16位段地址和16位偏移地址,合成20位物理地址
规则: 20位地址=段地址×16+偏移地址
CPU 可以用不同的段地址和偏移地址形成同一个物理地址
-
示例:
段地址:1000h,偏移地址:1234h
-
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 指令系统概述
-
机器指令:
用二进制编码表示的指令,由操作码和操作数两部分组成
-
汇编指令:
用指令助记符和符号地址表示的指令
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)
-
操作数的寻址方式:
重点:
- 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位数据)
-
示例
3.4 XCHG指令
-
格式:XCHG 操作数1,操作数2
-
功能:数据交换,操作数1与操作数2单元的内容互相交换
由于两操作数即是源操作数,又是目的操作数,故它们的位置顺序无关紧要
-
操作数的寻址方式:
重点:
- 不能有imm
- 源操作数必须是reg
- 类型必须一致且明确
-
示例
3.5 PUSH指令
-
格式:PUSH 源操作数
-
功能:数据进栈
- 源操作数为16位,(E)SP内容减2
- 源操作数为32位,(E)SP中内容减4
注意,栈操作单位只能是16/32位。8086CPU只能是16位。
即,不可 PUSH AL,只能PUSH AX。
-
操作数的寻址方式:
- 示例
3.6 POP指令
-
格式:POP 目的操作数
-
功能:从栈中弹出数据到目的操作数所确定的单元中
- 操作数为16位,(E)SP向高地址端调整2字节
- 操作数为32位,(E)SP向高地址端调整4字节
注意,栈操作单位只能是16/32位。8086CPU只能是16位。
即,不可 POP AL,只能POP AX。
-
操作数寻址方式:
- 示例
- 使用实例
1 | 使用PUSH和POP指令,将字(16位)单元[1000h]内容和字单元[2000h]内容分别放入EAX低16位和高16位。 |
3.7 XLAT指令
-
格式:XLAT
-
功能:查表换码。
将以(E)BX基址,以AL内容为位移量的字节单元内容传送给AL。
这条指令使用隐含操作数。在指令执行前约定:必须已经建立一个字节表,表首地址已经放入基址寄存器(E)BX;查找项的位移量已经放入AL
-
实例
3.8 地址传送指令LEA
- 格式:LEA 寄存器,源内存操作数
- 功能:有效地址送寄存器。将内存操作数的偏移地址(EA)传送至目的寄存器中。
- 操作数的寻址方式:
-
若是16位寄存器则只装入EA的低16位
-
实例
1 | (1) LEA AX, [EBX][ESI] |
3.9 地址传送指令2
-
格式:LDS/LSS/LES/LFS/LGS 目的寄存器,源内存操作数
-
功能:
将源内存操作数中的低2/4字节内容 存入 目的寄存器
高2字节内容 存入 段寄存器DS/SS/ES/FS/GS
-
操作数的寻址方式:
- 示例
3.10 标志位传送指令
1 | 1) LAHF指令 |
3.11 扩展指令
1 | CBW |
3.12 输入输出指令
- IN、OUT指令专门用来读写I/O端口
- 只能使用累加器(AL/AX/EAX)来接收、发送数据
1 | 1) IN指令 |
- 示例
3.13 DOS系统功能调用
- 介绍
1 | (1) 01h。输入一个字符,有回显。 |
3.14 汇编使用
- 前置操作
1 | MOUNT C: D:\MASM |
- 以编写的001.ASM为例介绍
1 | TYPE 001.ASM 查看类型信息 |
汇编ASM文件的文件名字不能过长,否则无法打开。
- DEBUG操作
1 | 查看和修改寄存器内容的命令 R |
第四课 源程序结构、运算指令
4.1 源程序结构
1 | _STACK SEGMENT STACK 'STACK' |
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 | (1) 作为立即数出现在指令中 |
(2)变量
-
变量是存放数据的内存单元名称
- 变量名是符号地址
- 具有类型属性:字节、字、双字…
-
定义格式:
1
2
3
4[变量名] 数据定义伪指令 初值表 [;注释]
DB、DW、DD、DF、DQ、DT
1B、2B、4B、6B、8B、10B类型-
分配一个或多个指定类型的内存单元,并可用变量表示该内存单元
-
如果有变量名,那么它仅代表所定义的数据存储区的第一个单元地址
-
-
定义的形式——初值表
-
常数或常数表达式
-
“?”形式:内容不确定。一般情况下,程序在汇编时以0填充。
-
符号地址及地址表达式:符号地址对应的地址编号,是无符号整型常数。
-
可用DUP把某项重复n次:其格式为
n DUP(数据项)
-
-
属性
-
地址属性
-
段地址可由运算符SEG返回
-
偏移地址可由运算符OFFSET返回
1
2MOV AX,SEG Msg ;将Msg的段地址送到AX
MOV BX,OFFSET Msg ;将Msg的偏移地址送到BX注:当需要存取某一变量时,必须先将该段的段地址放到相应的段寄存器(如DS、ES等)
1
2MOV AX, _DATA
MOV DS, AX -
-
类型属性
伪指令DB, DW, DD, DF, DQ, DT可定义1, 2, 4, 6, 8, 10字节类型
可以指定数据类型
1
2
3
4
5
6Byte Ptr 指定数据或变量为字节类型(8位)
Word Ptr 指定数据或变量为2字节(字)类型(16位)
DWord Ptr 指定数据或变量为4字节(双字)类型(32位)
FWord Ptr 指定数据或变量为6字节类型(48位)
QWord Ptr 指定数据或变量为8字节(四字)类型(64位)
TByte Ptr 指定数据或变量为10字节类型(80位)变量名对应的是内存单元地址,是无符号符号整常数,
加、减一个整常数就是地址编号加、减一个整常数,
仍然对应一个内存单元地址,其类型与原变量相同。
1
2
3L DD 1234A1B2h, 87654321h
L是4字节类型单元,L+1对应的也是4字节类型单元,其内容是211234A1h
L+1可以写成L[1],注意,L[1]是L的偏移地址加1而不是加1×4每种数据类型的类型值就是其占用的字节数,可用运算符TYPE将它分离出来
-
-
示例1
1 | Msg DB "Hello" ;定义变量,分配5个1B空间,置初值 |
- 示例2
1 | Msg DB "Hello" |
- 示例3
1 | Msg DB "Hello" 13, 10, "$" |
- 示例4
1 | s1 DB "你好", 2 DUP('!'), 2 DUP ('A', 'B'), 3 DUP(1, 2, 2 DUP('$')) |
- 示例5
1 | Variable DW 100 DUP(0) |
(3)标号
-
定义
-
标号表示的是指令在内存中存放的位置
-
标号定义的格式是:
1
标号名:
表示标号后首条指令在内存中地址。
标号既可以定义在目的指令同一行的最前面,
也可以在目的指令前一行单独用一行定义。
-
-
属性
-
地址属性
标号代表的是其后首条指令在内存中地址
- 用SEG来返回标号所在段的段地址
- 用OFFSET来返回标号所在段的偏移地址
1
2MOV 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 | bVariable LABEL BYTE |
- 示例2
1 | wVariable EQU THIS DWORD |
4.3 表达式和运算符
-
将常数、符号地址及其符号常量用运算符连接起来的有意义的式子
-
值的计算是在源程序汇编过程中完成的
-
运算符分为:
- 算术运算符
- 逻辑运算符
- 关系运算符
- 数值返回运算符
- 属性运算符
- 字节分离运算符
4.4 MASM的基本伪指令
伪指令在汇编程序对源程序汇编期间由汇编程序处理的操作,它们可以完成诸如定义程序段、定义数据、分配存储区和指示程序结束等功能。伪指令在形式上与一般指令相似,但伪指令只是为汇编程序提供有关信息,不产生相应的机器代码。
指令集选择伪指令
-
MASM在默认情况下只接受8086指令集。
如果程序员需要使用8086以后微处理器新增加的指令,必须使用指令集选择伪指令。
完整的段定义伪指令
-
使用完整的段定义伪指令来定义一个段,
可具体控制汇编程序MASM和连接程序LINK在内存中组织代码和数据的方式
-
格式
1 | 段名 SEGMENT [定位类型] [组合方式] [地址模式] ['分类名'] |
-
功能
在程序中定义一个逻辑段,指定段的名字和范围,段在内存中的起始位置,以及段与段之间的连接关系。
-
段指定伪指令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用在代码段中:
1
2
3
4ORG 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 | 子程序名 PROC [NEAR/FAR] |
子程序名是子程序的入口地址的符号表示,是符号地址,也具有地址属性和类型属性。
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 | 示例:定义了宏指令ExitToDos和InOutStr,并在在代码段中调用它们 |
4.9 算术运算指令
(1)加法
-
ADD指令
格式:ADD目的操作数,源操作数。
功能:将源操作数与目的操作数相加,结果存入目的操作数。1
2ADD reg, reg/mem/imm
ADD mem, reg/imm根据结果置CF, AF, PF, ZF, SF, OF的状态
-
ADC指令
格式:ADC目的操作数,源操作数。
功能:即将源操作数、目的操作数和CF相加,结果存入目的操作数。1
2ADC reg, reg/mem/imm
ADC mem, reg/imm根据结果置CF, AF, PF, ZF, SF, OF状态
-
INC指令
格式:INC 操作数。
功能:操作数自身加1,即将操作数加1,结果再存入操作数。1
INC reg/mem
影响AF, PF, ZF, SF和OF,不影响CF
(2)减法
-
SUB指令
格式:SUB 目的操作数,源操作数。
功能:目的操作数减去源操作数,结果存入目的操作数。1
2SUB reg, reg/mem/imm;
SUB mem, reg/imm。根据结果置CF, AF, PF, ZF, SF, OF的状态
-
SBB指令
格式:SBB 目的操作数,源操作数。
功能:目的操作数减去源操作数,再减去CF,结果存入目的操作数。1
2SBB reg, reg/mem/imm;
SBB mem, reg/imm。根据结果置CF, AF, PF, ZF, SF, OF的状态
-
DEC指令
格式:DEC 操作数。
功能:操作数自身减1,即操作数减去1,结果再存入操作数。1
DEC reg/mem
影响AF, PF, ZF, SF和OF,不影响CF
(3)求补
-
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)比较
-
CMP指令
格式:CMP 目的操作数,源操作数。
功能:两操作数比较大小,根据目的操作数减去源操作数的运算结果,从而设置标志位。1
2CMP 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 |
两者的区别在于:
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 |
两者的区别在于:
DIV的操作数是无符号数,商和余数均为无符号数;
IDIV操作数是补码,商和余数均为有符号数,余数符号与被除数符号相同。
-
可能导致两类错误:除数为零,商溢出。
-
当除法运算所得的商超过表示范围时,就产生商溢出。
-
除法指令对所有标志位无定义
4.10 逻辑运算指令
逻辑指令包括逻辑运算指令和移位指令
(1)逻辑运算指令
五个指令
-
逻辑运算是按位操作的,包括:AND, OR, NOT, XOR和TEST指令。
-
AND指令
1
2AND reg, reg/mem/imm
AND mem, reg/imm根据结果置SF, ZF和PF,CF=0,OF=0,AF无定义。
-
OR指令
1
2OR reg, reg/mem/imm
OR mem, reg/imm根据结果置SF, ZF和PF,CF=0,OF=0,AF无定义
-
NOT指令
1
NOT reg/mem
不影响标志位
-
XOR指令
1
2XOR reg, reg/mem/imm
XOR mem, reg/imm根据结果设置SF, ZF和PF,CF=0,OF=0,AF无定义
-
TEST指令
1
2TEST 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
-
逻辑移位
-
SHL指令
格式:SHL 目的操作数,移动位数。
功能:目的操作数逻辑左移,最后移出的位进入CF,最低位用0填充。1
SHL reg/mem, imm8/CL
影响CF, OF, SF, ZF, PF,而AF不确定。
OF在左移1位时有效,否则不确定。
左移1位后,若符号位改变,OF=1,否则OF=0 -
SHR指令
格式:SHR目的操作数,移动位数。
功能:目的操作数逻辑右移,最后移出的位进入CF,最高位用0填充。1
SHR reg/mem, imm8/CL。
影响CF, OF, SF, ZF, PF,而AF不确定。
OF在右移1位时有效,否则不确定。
右移1位后符号位改变,OF=1,否则OF=0
算术移位
(移位后正负号 不变 )
-
SAL指令
格式:SAL目的操作数,移动位数。
功能:算术左移指令。SAL与SHL是同一条指令,即一个操作码对应的两个助记符
-
SAR指令
格式:SAR 目的操作数,移动位数。
功能:目的操作数算术右移,最后移出的位进入CF,高位用符号位填充。1
SAR reg/mem, imm8/CL
影响CF, OF, SF, ZF, PF,而AF不确定。
OF在右移1位时有效,否则不确定。
右移1位后,OF=0
循环移位1
-
ROL指令
格式:ROL目的操作数,移动位数。
功能:目的操作数循环左移,最后移出的位进CF1
ROL reg/mem
影响CF, OF, SF, ZF, PF,而AF不确定。
OF在左移1位时有效,否则不确定。
左移1位后,若符号位改变,OF=1,否则OF=0 -
ROR指令
格式:ROR 目的操作数,移动位数。
功能:目的操作数循环右移,最后移出的位进CF。1
ROR reg/mem, imm8/CL
影响CF, OF, SF, ZF, PF,而AF不确定。
OF在右移1位时有效,否则不确定。
右移1位后,若符号位改变,OF=1,否则OF=0
循环移位2
-
RCL指令
格式:RCL 目的操作数,移动位数。
功能:目的操作数和CF一起循环左移。1
RCL reg/mem, imm8/CL
影响CF, OF, SF, ZF, PF,而AF不确定。
OF在左移1位时有效,否则不确定。
左移1位后,若符号位改变,OF=1,否则OF=0 -
RCR指令
格式:RCR目的操作数,移动位数。
功能:目的操作数和CF一起循环右移。1
RCR reg/mem, imm8/CL
影响CF, OF, SF, ZF, PF,而AF不确定。
OF在右移1位时有效,否则不确定。
右移1位后,若符号位改变,OF=1,否则OF=0
第五课 转移、选择、循环
5.1 转移指令
-
CS:(E)IP
指向下一条要执行的指令在内存中的地址,控制转移指令实际上通过改变
CS:(E)IP
来达到控制程序的执行流程。包括:
- 无条件转移指令
- 条件转移指令
- 循环指令
- 子程序调用和返回指令
- 中断调用和中断返回指令
JMP,JccCALL,RETINT,IRET
-
转移
- 仅能改变
(E)IP
,是近转移或段内转移 - 能改变
(E)IP
和CS
,是远转移或段间转移
- 仅能改变
(1)无条件转移
-
格式:
1
JMP 目标地址
-
功能:无条件转移到目标地址,执行从该地址开始的指令序列
1 | MOV AX, A |
(2)条件转移
-
条件
- 条件满足时则转移,条件不满足时,则顺序执行后面的指令
- 具体条件见下面的指令
- 单个标志位的条件
- 无符号数比较的条件
- 有符号数比较的条件
-
格式
常用Jcc来代表这类指令的助记符
1
Jcc 标号
条件转移指令都是段内直接近转移
单个标志位
- Z、S、C、O、P均有各种的指令
简记:
记住
JE
、JNE
为相等则跳转、不相等则跳转。
无符号数比较
- 无符号数比较时,根据CF来判断大小
简记:
- A为无符号大
- B为无符号小
- 拼加N、E
有符号数比较
- 有符号数比较时,根据SF和OF来确定大小
简记:
- G为有符号大 Greater
- L为有符号小 Less
- 拼加N、E
5.2 循环指令
-
主要使用LOOP指令比较多
-
LOOP会以
CX
寄存器存着的值作为循环次数,进行循环跳转执行到
LOOP LOC1
,会先执行
CX - 1
,再判断:
CX
不等于0:跳转到LOC1
位置执行CX
等于0:循环结束,执行LOOP LOC1
后面的语句
5.3 其他指令
(1)标志位处理指令
(2)其他指令
第六课 子程序调用
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,则还要执行
与CALL指令配套使用,实现从子程序中返回,继续主程序的执行 。
6.4 寄存器的保护与恢复
-
主程序和子程序通常是分别编制的,所以它们所使用的寄存器往往会发生冲突。
-
进入子程序后,把子程序所需要使用的寄存器内容保存在栈中,此过程称作寄存器保护
-
在退出子程序前把寄存器内容恢复原状,此过程称作寄存器恢复
-
-
寄存器保护与现场恢复分别使用压栈和出栈指令实现
1 | CRLF PROC Far ;子程序定义开始,属性类型为Far |
根据需要,可用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
31foo 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
19f 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
6.8 多模块程序设计
-
一个汇编语言程序可以划分成主模块和多级、多个子模块。
-
每个模块的源程序都是以END伪指令来结束,
但是只有主模块中的END后可以接标号,以表示程序的入口地址,
其他模块中的END后不能有标号。
全局符号与外部符号
- PUBLIC伪指令与EXTRN伪指令配套使用。
-
全局符号名说明伪指令PUBLIC
格式:1
PUBLIC符号名1[, 符号名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 StartM1.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 ;模块至此结束 -
连接方法
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个中断向量,称为中断向量表。
-
实地址模式下的中断向量表
-
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 输入/输出
-
用IN、 OUT指令与外部设备交换信息
端口
-
与I/O设备间数据传送方式:
- 程序直接控制I/O方式
- 中断传送方式
- 直接内存存取(DMA)
7.3 中断处理程序的主要步骤
1 | (1) 保存寄存器的内容; |
-
中断服务程序的入口地址只有放到中断向量表中,才能在中断发生时被调用到。
所以,除了中断处理程序,还有相应设置中断向量及相应的硬件初始化工作。
补录:实用示例
1. 打印可见的十六进制数
-
题目
16位二进制数存在BX,以可见字符形式输出打印在屏幕上
-
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22PUBLIC 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中 -
代码
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