第一章 基础知识
1.1 汇编语言的一般概念
计算机程序设计语言可分为机器语言、高级语言、汇编语言三类。
1.机器语言
用二进制数码表示控制计算机的命令和各种数据的一种程序设计语言。
2.高级语言
屏蔽具体操作细节,使用类似于自然语言的一些语句来编译程序,完成指定任务。
3.汇编语言
使用字母和符号来表示机器语言的命令,用十进制数或十六进制数来表示数据的计算机程序设计语言。
汇编语言程序与机器语言程序的关系:一条汇编语言的语句与一条机器语言指令对应,汇编语言程序与机器语言程序效率相同。
1.2 学习和使用汇编语言的目的
在计算机系统中,某些功能必须用汇编语言程序来实现,汇编语言程序的效率高于高级语言程序。
效率有两方面的含义:程序的目标代码长度和运行速度。
1.3 进位计数制及其相互转换
1.进位计数制
使用一定个数的数码的组合来表示数字的表示方法被称为进位计数制。将一个数的各个位置上所表示的基本数值称为位权,简称权。例如十进制数222位权从低到高即为1,10,100。每个数位上能使用不同数码的个数称为基数,例如十进制有十个数码0~9,因此基数为10。
二进制与8进制和16进制之间的转换关系:3位二进制数与一位8进制数对应,4位二进制数与一位16进制数对应。
在书写不同进位计数制数时,为了区别,在数的尾部用一个字母来表示对应的进制
B(Binary)——二进制数
O(Octal)或Q ——八进制数
D(Decimal)——十进制数
H(Hexadecimal)——十六进制数
若未使用字母,则默认为十进制数
2.各种数制间的相互转换
二进制与8进制和16进制之间的转换关系在前文已提到,此处不再赘述。
<1>十进制整数转换为二进制数
十进制整数转换为二进制数有两种方法:
(1)减权定位法
从二进制数高位起,依次用待转换的十进制数与各位权值进行比较;
如果够减,则该数位系数Ki=1,同时减去该位权值,余数作为下一次比较的值,如果不够减,则该数位系数ki=0
例如:将325转换为二进制数
因为2的九次方(512)大于325,325大于2的八次方(256),因此从K8位开始比较,如下
512>325>256 325-256=69 K8=1
128>69>64 K7=0
69>64 69-64=5 K6=1
32>5 K5=0
16>5 K4=0
8>5 K3=0
5>4 5-4=1 K2=1
2>1 K1=0
1=1 1-1=0 K0=1
所以325D=101000101B
(2) 除基取余法
将十进制数除以基数2,其余数为二进制数的最低位,再用其商除2,其余数为次低位,反复直到商为0
还是以将325转换为二进制数为例:
325÷2=162+1 K0=1
162÷2=81 K1=0
81÷2=40+1 K2=1
40÷2=20 K3=0
20÷2=10 K4=0
10÷2=5 K5=0
5÷2=2+1 K6=1
2÷2=1 K7=0
1÷2=0+1 K8=1
另外,此种方法也适用于其它进制之间的转换
<2>十进制小数转换为二进制数
(1)减权定位法
注意,转换时应根据程序要求的精度或计算机的字长确定二进制的位数。
(2)乘积取整法
将十进制数乘基数2,其积的整数部分为二进制数的最高位,再用其小数部分乘2,其积整数部分为次高位,反复直到小数部分为0
例:将0.8125D转换为二进制数
0.8125×2=1.625>1 K-1=1
0.625×2=1.25>1 K-2=1
0.25×2=0.5<0 K-3=0
0.5×2=1=1 K-4=1
所以0.8125D=0.1101B
<3>二进制整数转换为十进制数
(1)按权相加法
(2)逐次乘基相加法
从二进制数的最高位开始,将最高位的数乘以2并与次高位的数相加,之后将结果乘以2再与低一位相加,重复此过程直至最低位加入结果。
<4>二进制小数转换为十进制数
(1)按权相加法
(2)逐次除基相加法
从二进制数的最低位开始,将最低位的数除以2并与次低位的数相加,之后将结果除以2再与高一位相加,重复此过程直至最高位加入结果。
1.4 带符号数的表示
计算机中使用0,1来表示正负,0表示正,1表示负,这种数也被称为机器数。由+,—来表示正负的是真值。带符号的机器数可以用原码,补码,反码三种码制来表示
1.原码
二进制数的最高位表示符号,0表示正,1表示负。数值部分用二进制数绝对值表示
8位二进制数原码最大数为01111111(+127),最小数为11111111(-127)
8位二进制数表示范围:-127 ≤ X ≤ +127
0的原码有两种表示形式:00000000(+0)和10000000(-0)
2.补码
带符号数X的补码表示为[X]补,定义:[X]补 = M+X(Mod M),其中M根据机器数的位数而定,如位数为八则表示M为2的八次方。符号位仍然是0为正,1为负
由真值、原码转换为补码
对于正数,补码与原码相同,对于负数,将真值的各位取反再加一即可得到补码;或者将原码的符号位保持不变,其余各位取反再加一即可得到补码
8位二进制数补码最大数为01111111 = [+127]补,最小数为10000000 = [-128]补
0的补码只有一个,即00000000
[X+Y]补 = [X]补+[Y]补
[X-Y]补 = [X]补-[Y]补 = [X]补+[-Y]补
1.5 字符的表示
1.ASCII码
标准ASCII码共有128个,可分为两类:
非打印ASCII码,共33个,用于控制操作,如BEL(响铃07H),DEL(删除7FH),CR(回车,0DH),LF(换行,0AH)。
可打印ASCII码共有95个,如数字符0~9,大小写字母等
1.6 基本逻辑运算
计算机内部采用二进制数表示信息,而“0”和“1”正好对应逻辑“真”和“假”两个状态。
四种逻辑计算:
“与”运算(AND)
也叫做逻辑乘,常用∧或⋅表示
“或”运算(OR)
也叫逻辑加,用∨或+表示“非”运算
指对逻辑变量取相反的一个逻辑值,若A为1,则A¯ = 0“异或”运算
通常用⨁表示,即 F=A⨁B,A与B相同时,F为1,否则F为0
第二章 IBM-PC微机的功能结构
2.1 IBM-PC微机基本结构
1.微机的一般构成
这里的微机指的就是计算机
计算机的组成
计算机一般由5部分组成,运算器,控制器,存储器,输入设备,输出设备
将运算器和控制器两大部件集成在一个集成电路芯片上,称为中央处理器 也叫CPU,
系统采用的是总线结构,具有较大的灵活性和扩展性
中央处理器
微型计算机的中央处理器包括运算器和控制器,主要的功能就是分析从主存储器取来的各条指令的功能,控制计算机的各个部件完成指定功能的各项操作。
主存储器
主存储器是用来存放程序和数据的部件,他由若干个存储单元组成,每个存储单元使用一个唯一的编号来标识(存储单元地址),对每个存储单元的操作就是通过这个地址来实现的。
计算机存储信息的基本单位是一个二进制的位,一个位可以存储一个二进制数 0或1,每8位组成一个字节,在大多数的计算机中存储器的组成都是以字节为基本单位的也称为一个存储单元。
习惯上把CPU和主存储器合称为主机,在计算机中除了主存储器之外,一般还有辅助存储器,也称为外存。
输入和输出设备及接口
输入设备将外部的信息送入计算机,包括鼠标和键盘
输出设备将处理后的结构转换为人或者系统能识别的信息向外输出。
由于I/O设备的工作速度,工作原理以及所处理的信息格式等与主机相差很大,所以I/O设备要通过I/O接口才能与系统总线相连接。
I/O接口是主机与I/O设备之间的逻辑控制组件,通过他实现主机和I/O设备间的信息传递
有的设备既有输入功能也有输出功能,如磁盘,磁带 触摸显示屏等
系统总线
微机的个个部分肯定不是单独的工作的,要大家一起协调工作,这里边就需要通信的问题
而,系统总线就是将微机的个个部分相连接,实现各大部件之间的信息传递
系统总线包括地址总线,数据总线,控制总线,他们分别用来传递不同的信息
2.Intel 8086/8088 CPU的功能结构
汇编语言程序是由一系列的指令(指令序列)构成,指令是构成汇编语言程序的基本单位,就像高级语言的语句
CPU执行指令序列就是重复执行一下两个步骤 1 从存储器中取指令。2 执行指令规定的操作
这两个步骤执行又分为串行方式和指令流水线方式
串行方式的特点是当CPU在执行指令的阶段,不需要占用系统总线,但是此时系统总线也不工作,因此系统总线的空闲时间较多。
在存储区取指令,取数据或者是存数据是,总线处于忙状态,其占用的时间较长,而CPU却只需要很短的处理时间,因此大部分时间处于闲置状态
所以采用串行工作方式的计算机其运行速度较慢要实现指令流水线方式,从CPU组件结构上要划分成多个单元。8086/8088CPU被分成了两个单元 ,EU和BIU是既分工又合作的两个独立的部分,他们的操作在一定的程度上是并行的,从而大大加快了指令执行速度
执行单元EU的任务是分析和执行指令,从指令队列中取出指令代码,由控制器译码产生相应的控制信号,控制个部件完成指令规定的操作,对操作数执行分钟指令指定的算术或逻辑运算,向地址总线接口单元BIU发送访问主存或者I/O的命令,并提供相应的地址和传送的数据
BIU负责CPU与存储器,I/O的信息传送,取指令:根据CS寄存器和指令指针IP形成的20位的物理地址,从相应的存储器单元中取出指令,暂存到指令队列中,等待EU取走并执行,存数据:在EU执行指令的过程中,需要与存储器或者是I/O端口传送数据,由EU提供的数据和地址结合段寄存器,通过外部总线与存储器或I/O进行数据的存取。
2.2 Intel 8086/8088CPU寄存器结构及其用途
通用寄存器8个+控制寄存器2个+段寄存器4个
通用寄存器:Intel有8个16位通用寄存器,他们具有良好的通用性,并且还可以用作某个特定的功能,可以由程序设计人员编程访问。
数据寄存器:它包括AX,BX,CX,DX四个寄存器,他们中的每一个寄存器既可以是一个16位的寄存器,也可以是分成两个8位的寄存器,即可以当作8个独立的8位寄存器使用。
数据寄存器既可以用来存放参加运算的操作数,也可以存放运算的结果
指针寄存器:指针寄存器包括堆栈指针SP和基址指针BP,SP指针-在执行堆栈操作的时候隐含使用,被用来指向堆栈顶部单元,BP指针-被用来指向堆栈内某一存储单元。
他们一般被用来存放16位地址,在形成20位物理地址的时候常被作为偏移量使用变址寄存器:有两个16位的变址寄存器 SI和DI,一般被用来作为地址指针
SI:源变址寄存器 DI:目的变址寄存器
段寄存器
cpu在使用存储器的时候,将它分成了若干个段。每段用来存放不同的内容,如程序代码,数据等等,每个存储段用一个段寄存器指明该段的起使位置
CPU在访问存储器时候必须指明两个内容
1 所访问的存储单元是那个段,既明确使用的段寄存器,
2 该存储单元与段基址相聚多少
在程序设计中,一个程序将存储器划分成多少分是任意的,但是在程序运行的任意时刻最多只有4段,分别由CS,DS,ES,SS指定
指令指针IP
CPU在从存储器取指令的时候,以段寄存器CS作为代码段的基址指针,以IP为内容的偏移量,共同形成一条指令的存放地址,当CPU从内存中取出一条指令后,IP内容会自动修改为指向下一条指令。
标志寄存器
标志寄存器是用来反映CPU在程序运行时的某些状态,如是否有进位,奇偶性,结果的符号等等。
2.3 存储器的组织结构
存储器的组成
存储器是由若干个存储单元组成的,存储单元的多少表示了存储器的容量
每个存储单元存放相同长度的二进制数,一个存储单元的长度一般为8位二进制数,即一个字节
每个存储单元都有一个唯一的地址编号–地址
8086/8088CPU有20根地址线,即他可以产生20位的地址码,他的存储器的寻址能力位2的20次方,即1兆字节空间
字单元:一个字单元存储的是长度为16位二进制数,即两个字节,字单元的地址为两个字节单元中较小地址字节单元的地址。
在定义一个地址的时候必须指出使用的是字节或字类型属性
存储器的段结构
由于8086/8088可寻址的存储空间为1MB。需要提供20位的地址码,而CPU内部的寄存器长度为16位,能够直接访问的最大的地址空间为64KB
存储器结构的特点
8086/8088CPU将1M的存储空间划分成若干个段,每段最大的长度为64K个字节单元组成
存储器中规定从0地址开始,每16个字节称为一个小节,因此1M内存就可以划分为64K个小节,和寄存器的寻址空间就对应上了。
每个小节的首地址最低位必为0,因此段基址只能是上述64K个小节首址之一。
逻辑段在物理存储器中是可以邻接的,间隔的,部分重叠或者完全重叠等4种情况
在任一时刻,一个程序只能访问4个当前段中的内容,分别是代码段,数据段,堆栈段,附加段,他们分别保存的是段基址的高16位地址,称为段基值。段基址的最低4位为0
逻辑地址与物理地址及对应关系
物理地址:在1M的存储空间中,每个存储单元的物理地址是唯一的,他就是该存储单元的20位地址
CPU与存储器之间的任何信息交换都是通过使用物理地址
逻辑地址:在程序设置中,为了便于程序开发和对存储器进行动态的管理,使用了逻辑地址。一个逻辑地址包含两个部分,段基值和偏移量
段基值:存放在某一个寄存器中,是逻辑地址段开始单元的高16位
偏移量:表示某个存储单元与他所在的段的段基址之间的字节距离
逻辑地址和物理地址的转化
将逻辑地址的段基值左移4位,形成20位的段基址,然后和16位的偏移量相加得到的就是物理地址
逻辑地址的来源
序号 操作类型 逻辑地址
段基值 偏移量(OFFSET)
隐含来源 允许替代来源
1 取指令 CS 无 IP
2 堆栈操作 SS 无 SP
3 取源串 DS CS,SS,ES SI
4 存目的串 ES 无 DI
5 以BP为基址 SS CS,DS,ES 有效地址EA
6 存取一般变量 DS CS,SS,ES 有效地址EA
如果允许替代来源,就叫做段超越,它表示段基值除使用隐含的段寄存器外是否可以指定其他段寄存器来提供
有效地址EA表示根据指令所采用的寻址方式计算出来的段内偏移量
2.4 堆栈及其操作方法
堆栈是一个特定的存储区,访问该存储区一般需要按照专门的规则进行操作
堆栈的用途:主要用于暂存数据以及在过程调用或处理中断时保存断点信息
堆栈的构造
堆栈一般分为:专用堆栈存储器和软件堆栈
专用堆栈存储器:按堆栈的工作方式专门设计的存储器。
软件堆栈:由程序设计人员用软件在内存中划出的一片存储区作为堆栈来使用。
堆栈的一端是固定的,称为栈底,是存储器的最大地址单元
另一端是浮动的,称为栈顶,随着堆栈中存放信息的多少而改变
为了确定栈顶的位置,通常使用堆栈指针SP指示栈顶 堆栈中所有数据进出都由SP来控制
堆栈存取数据的规则:“先进后出FILO”
8086/8088堆栈的组织
在808/8088微机中堆栈是由堆栈寄存器SS指示的一段存储区域
当SP被初始化时,指向栈底+2单元,其值就是堆栈的长度 顶由堆栈指针SP指示,SP中内容始终表示堆栈段基址与栈顶之间的距离(字节数)
指向栈底+2单元的原因是当栈为空的时候,栈中没有元素,也就不存在栈顶元素,所以SP只能指向栈的最底部单元下面的单元,该单元的偏移地址为栈最底部的字单元的偏移地址+2
通过改变堆栈段寄存器SS的内容,即可改变到另一个堆栈段,当改变了堆栈段寄存器SS的内容,必须紧接着赋予SP新值。
数据在堆栈中存放格式:以字(两字节)为单位存放,数据的低8位放在较低地址单元,高8位放在较高地址单元
堆栈操作
设置堆栈
例如:STACK1 SEGMENT PARA STACK
DB100 DUP (0) STACK1 ENDS
PARA STACK说明本段为堆栈段
进栈PUSH
例如:PUSH AX;将寄存器AX的内容压入堆栈
PUSH DS;将段寄存器DS的内容压入堆栈
PUSHF;将标志寄存器内容压入堆栈
进栈过程:首先将堆栈指针SP-2,即指向一个空的堆栈字单元。之后将要储存的内容送入SP指向的字单元中
出栈POP
出栈操作由POP指令或机器自动实现,它从堆栈顶部弹出一个字到通用寄存器、段寄存器或字存储单元
例如:POP AX;将栈顶字单元内容弹出到AX
POP DS;将栈顶字单元内容弹出到段寄存器DS
POPF;将栈顶字单元内容送回标志寄存器F
出栈过程:首先将SP指向的字单元内容送往指定的寄存器或存储器。之后将堆栈指针SP内容+2
第三章 寻址方式与指令系统
3.1 寻址方式
一条指令由操作码和操作数两部分组成
操作码:表示该指令应完成的具体操作
操作数:操作码的操作对象
寻址方式:寻找指令中所需操作数的各种方法,也就是提供指令中操作数的存放信息的方式
Intel 8086/8088各指令中提供操作数的方法有一下四种:
立即数操作数——操作数在指令代码中提供。立即数可以是8位,也可以是16位
寄存器操作数——操作数在CPU的通用寄存器或段寄存器中
存储器操作数——操作数在内存的存储单元中 I/O
端口操作数—— 操作数在输入/输出接口的寄存器中
存储器存储单元的逻辑地址由端基值和偏移量组成。
偏移量标识了该存储单元与段起始地址之间的距离,也叫做有效地址EA。
有效地址EA是位移量、基址和变址这三个地址分量的几种组合,由CPU的执行单元EU计算出来的。
位移量:指令中直接给出的一个8位或16位数,一般源程序中以操作数名字(变量名或标号)的形式出现
基址:由基址寄存器BX或基址指针BP提供的内容
变址:由源变址寄存器SI或目的变址寄存器DI提供的内容
直接寻址
指令由操作码+操作数组成,操作数的有效地址EA只有位移量地址分量
在汇编语言源程序中,直接寻址方式用符号或常数来表示
用符号表示
MOV BX,VAR;
表示将数据段中,偏移了VAR个字节距离的字单元内容送到寄存器BX中
用常数表示
MOV AX,DS:[64H](不加方括号则表示将100送到AX中)
表示从当前数据段开始,偏移100个字节的字单元内容送到AX中。不能写为MOV AX,64H,一定要带方括号
寄存器间接寻址
指令由操作码+操作数[BX|BP|SI|DI]组成 操作数有效地址EA直接从基址寄存器(BX或BP)或变址寄存器(SI或DI)中获得
寄存器间接寻址就是事先将偏移量存放在某个寄存器(BX、BP、SI或DI)中,这些寄存器就如同一个地址指针
在程序运行期间,只要对寄存器内容进行修改,就可以实现用同一条指令实现对不同存储单元进行操作
当指令中使用的是BP寄存器,则隐含表示使用SS段寄存器,其余情况则隐含使用DS段寄存器
基址寻址/变址寻址
操作数的有效地址EA等于基址分量或变址分量加上指令中给出的位移量
指令由操作码+寻址特征[BX|BP|SI|DI]+位移量组成,指令中使用BX或BP时为基址寻址,指令中使用SI或DI时为变址寻址。
段寄存器的隐含使用规则与间接寻址方式相同
这两种寻址方式只需通过改变寄存器的内容就可用一条指令访问不同的存储单元,并且由于增加了一个位移量分量,因此它们能够很方便地访问数组和表格数据
由于这两种方式中寄存器的内容是相对于由位移量指定的初始单元,因此也叫寄存器相对寻址。
基址变址寻址
操作数的有效地址是三个地址分量之和,即:EA = 基址+变址+位移量 指令由操作码+寻址特征[BX|BP][SI|DI]+位移量
在此方式中,程序运行期间有两个地址分量可以修改。因此此方式是最灵活的一种寻址方式,可以方便地对二维数组进行访问
串操作寻址方式
在寻找源操作数时,隐含使用SI作为地址指针。 在寻找目的串时,隐含使用DI作为地址指针。
在串操作完成之后,自动对SI和DI进行修改,使它们指向下一个操作数。 I/O端口寻址
在计算机系统,对I/O端口的寻址方式有存储器编址方法和I/O端口编址方法两种方法
存储器编址方法
将I/O端口视为存储器的一个单元,对端口的访问就如同访问存储单元一样。访问存储器的指令和各种寻址方式同样适用对I/O端口的访问
I/O端口编址方法(8086/8088系统采用方式)
I/O端口的地址与存储器地址分开,并使用专门的输入指令和输出指令
可以最多访问64K个字节端口或32K个字端口,用专门的IN指令和OUT指令访 问。寻址方式有直接端口寻址和寄存器间接端口寻址两种
直接端口寻址
在指令中直接给出端口地址,端口地址一般采用2位十六进制数,也可以用符号表示。直接端口寻址可访问的端口数为0~255个
例如:IN AL,25H;
寄存器间接端口寻址
把I/O端口的地址先送到DX中,用DX作间接寻址寄存器
例如:MOV DX,378H
OUT DX,AL
如果访问的端口地址值大于255,则必须用I/O端口的间接寻址方式
3.2 指令系统
一种计算机所能执行的各种类型的指令的集合称为该计算机的指令系统
从指令的格式划分,一般可以分为三种:
双操作数指令:OPR DEST SRC
单操作数指令:OPR DEST
无操作数指令:OPR
对于无操作数指令,包含两种情况:
指令不需要操作数,如暂停指令HLT
在指令格式中,没有显式地指明操作数,但是它隐含指明了操作数的存放地方,如指令PUSHF
Intel8086/8088CPU指令系统的指令可以分为六大类:
传送类指令
传送类指令的作用是将数据信息或地址信息传送到一个寄存器或存储单元中,可以分为以下四种情况:
通用数据传送指令。指令格式:MOV DEST,SRC。将源操作数指定的内容传送到目的操作数
交换指令。指令格式:XCHG DEST,SRC。将源操作数和目的操作数两者内容相互交换
标志传送指令
取标志寄存器指令。指令格式:LAHF。
存储标志寄存器指令。指令格式:SAHF。
标志进栈指令。指令格式:PUSHF。
标志出栈指令。指令格式:POPF。
地址传送指令
这类指令有两类3条,它们的作用是将存储单元的地址送寄存器
装入有效地址。指令格式:LEA DEST,SRC。
装入地址指针指令。指令格式:LDS DEST,SRC; LES DEST,SRC;
算术运算类指令
8086/8088指令系统中有加、减、乘、除指令,这些指令可以对字节数据或字数据进行运算。参加运算的数可以是无符号数,也可以是带符号数,可以是二进制数,也可以是是十进制数(以BCD码表示)。带符号数用补码表示
加法指令。指令格式:ADD DEST,SRC。
带进位加法指令。指令格式:ADC DEST,SRC
加1指令。指令格式:INC DEST
减法指令。指令格式:SUB DEST,SRC
带借位减法。指令格式:SBB DEST,SRC
减1指令。指令格式:DEC DEST
求负数指令。指令格式:NEG DEST
位操作类指令
逻辑运算指令
共有四条,这4条指令都是执行按位逻辑运算。指令格式分别是:
逻辑“与”指令 AND DEST, SRC
逻辑“或”指令 OR DEST, SRC
逻辑“异或”指令 XOR DEST, SRC
逻辑“非”指令 NOT DEST
测试指令。指令格式:TEST DEST, SRC
功能与AND指令相似,实现源操作数与目的操作数进行按位“逻辑与”运算,对标志位的影响与AND指令相同,但运算的结果不送入目的操作数,即目的操作数内容也将保持不变。主要用于测试某一操作数的一位或几位的状态
移位/循环移位指令
这一类指令共有8条,分为3类:
算术移位
算术左移 SAL DEST,COUNT
算术右移 SAR DEST,COUNT
逻辑移位
逻辑左移 SHL DEST,COUNT
逻辑右移 SHR DEST,COUNT
循环移位
小循环
循环左移 ROL DEST,COUNT
循环右移 ROR DEST,COUNT
大循环
带进位循环左移 RCL DEST,COUNT
带进位循环右移 RCR DEST,COUNT
串操作类指令
程序转移类指令
处理器控制类指令
标志位操作指令
清除进位标志CLC:置CF为0
置1进位标志STC:置CF为1
进位标志取反CMC:CF的值取反
清除方向标志CLD:置DF为0
置1方向标志STD:置DF为1
清除中断标志CLI:置IF为0
置1中断标志STI:置IF为1
与外部事件同步的指令
HLT:暂停指令
WAIT:等待指令
ESC:外部协处理器指令前缀
LOCK:总线锁定指令
空操作指令NOP:执行一次NOP占用CPU三个时钟周期,它不改变任何寄存器或存储单元内容,主要用于延时
3.3 指令编码
汇编:将汇编语言程序转换为机器语言程序的过程
汇编程序:在计算机中实现汇编过程的系统程序
Intel8086/8088汇编指令的编码格式有四种基本格式:
双操作数指令编码格式
单操作数指令编码格式
与AX或AL有关的指令编码格式
其它指令编码格式
第四章 汇编语言程序格式
4.1 汇编语言语句种类及其格式
汇编语言的语句可以分为指令语句和伪指令语句
指令语句:每一条指令语句在汇编时都要产生一个可供CPU执 行的机器目标代码,它又叫可执行语句。一条指令语句最多可以包含4个字段:
标号字段
指令助记符字段
操作数字段
注释字段
伪指令语句:又叫命令语句。伪指令本身并不产生对应的机器目标代码。它仅仅是告诉汇编程序对其后面的指令语句和伪指令语句的操作数应该如何处理。一条伪指令语句可以包含四个字段:
符号名字段
符号名后面没有冒号“:”,这是与指令语句的重要区别
伪指令符字段
规定了汇编程序所要完成的具体操作
操作数字段
该字段是否需要,以及需要几个是由伪指令符字段来决定
注释字段为可选项,该字段必须以分号开始。其作用与指令语句的注释字段相同
标识符
指令语句中的标号和伪指令语句中符号名统称为标识符。标识符是由若干个字符构成的
4.2 汇编语言数据
数据是指令和伪指令语句中操作数的基本组成部分。一个数据由数值和属性两部分构成
在汇编语言中常用的数据形式有:常数、变量和标号
常数:在汇编期间其值已完全确定,并且在程序运行过程中,其值不会发生变化
变量:变量用来表示存放数据的存储单元,这些数据在程序运行期间可以被改变
程序中以变量名的形式来访问变量,因此,可以认为变量名就是存放数据的存储单元地址
变量的定义与预置
定义变量就是给变量在内存中分配一定的存储单元。定义变量使用数据定义伪指令DB、DW、DD、DQ和DT等
当变量被定义后,就具有了以下三个属性:
段属性:表示变量存放在哪一个逻辑段中
偏移量属性:表示变量所在位置与段起始点之间的字节数
段属性和偏移量属性就构造了变量的逻辑地址
类型属性:表示变量占用存储单元的字节数
在变量的定义语句中,给变量赋初值的表达式可以使用下面4种形式:
数值表达式。例如:DATA1 DB 32,30H(DATA1的内容为32(20H),DATA1+1单元内容为30H)
?表达式
不带引号的问号“?”表示可以预置任意内容
字符串表达式
对于DB伪指令,字符串为用引号括起来的不超过255个字符。给每一个字符分配一个字节单元。字符串按从左到右,将字符的ASCII编码值以地址递增的排列顺序依次存放
DUP表达式
DUP称为重复数据操作符。使用DUP表达式的一般格式为:
变量名 + {DB|DW|DD} + 表达式1 DUP (表达式2)
其中,表达式1是重复的次数,表达式2是重复的内容
DUP还可以嵌套使用,即表达式2又可以是一个带DUP的表达式
变量的使用
在指令语句中引用
在指令语句中直接引用变量名就是对其存储单元的内容进行存取。当变量出现在变址(基址)寻址或基址变址寻址的操作数中时表示取用该变量的偏移量
在伪指令语句中引用
表示取变量地址的偏移量
标号
标号写在一条指令的前面,它就是该指令在内存的存放地址的符号表示,也就是指令地址的别名
标号主要用在程序中需要改变程序的执行顺序时,用来标记转移的目的地,即作转移指令的操作数
每个标号具有三属性:
段属性(SEG):即段基值
偏移量属性(OFFSET):即地址的偏移量
距离属性(也叫类型属性):表示该标号可以被段内还是段间的指令调用
NEAR(近):该标号只能作段内转移
FAR(远):该标号可以被非本段的转移和调用指令使用
4.3 符号定义语句
在源程序设计中,使用符号定义语句可以将常数或表达式等内容用某个指定的符号来表示。在8086/8088汇编语言中有两种符号定义语句:
等值语句。语句格式:符号名 EQU 表达式
表达式可以是任何形式,常见以下几种:
常数或数值表达式
地址表达式
变量、寄存器名或指令助记符
等号语句。语句格式:符号名=表达式
注意:等值语句与等号语句都不会为符号分配存储单元。 因此所定义的符号没有段、偏移量和类型等属性
4.4 表达式与运算符
表达式是指令或伪指令语句操作数的常见形式。它由常数、变量、标号等通过操作运算符连接而成
注意:任何表达式的值在程序被汇编的过程中进行计算确定,而不是到程序运行时才计算
8086/8088宏汇编语言中的操作运算符可以分为五类:
算术运算符
+、—、*、 / 、MOD、SHL、SHR、[ ]
“SHR ”和“SHL”为逻辑移位运算符
下标运算符“[ ]”具有相加的作用。一般使用格式: 表达式1 [表达式2]。作用:将表达式1与表达式2的值相加后形成一个存储器操作数的地址
逻辑运算符
逻辑运算符有NOT、AND、OR和XOR等四个,它们执行的都是按位逻辑运算
关系运算符
关系运算符包括:EQ(等于)、NE(不等于)、LT(小于)、 LE(小于等于)、GT(大于)、GE(大于等于)
数值返回运算符
该类运算符有5个,它们将变量或标号的某些特征值或存储单元地址的一部分提取出来
SEG运算符:取变量或标号所在段的段基值
OFFSET运算符:取变量或标号在段内的偏移量
TYPE运算符:取变量或标号的类型属性,并用数字形式表示。对变量来说就是取它的字节长度。
LENGTH运算符:取变量的长度。
如果变量是用重复数据操作符DUP说明的,则LENGTH运算取外层DUP给定的值。
如果没有用DUP说明,则LENGTH运算返回值总是1
SIZE运算符:只能作用于变量,SIZE取值等于LENGTH和TYPE两个运算符返回值的乘积
属性修改运算符
这一类运算符用来对变量、标号或存储器操作数的类型属性进行修改或指定
PTR运算符。使用格式:类型 PTR 地址表达式
功能:将地址表达式所指定的标号、变量或用其它形式表示的存储器地址的类型属性修改为“类型”所指的值。这种修改是临时的,只在含有该运算符的语句内有效
HIGH/LOW运算符。使用格式:{HIGH|LOW} 表达式
功能:如果表达式为一个常量,则将其分离成高8位和低8位;如果表达式是一个地址(段基值或偏移量)时,则分离出它的高字节和低字节
THIS运算符
功能:一般与等值运算符EQU连用,用来定义一个变量或标号的类型属性。所定义的变量或标号的段基值和偏移量与紧跟其后的变量或标号相同
运算符的优先级
不同的运算符具有不同的运算优先级别
优先级别 运算符
(最高)1 LENGTH,SIZE ,圆括号
2 PTR,OFFSET,SEG,TYPE,THIS
3 HIGH,LOW
4 *,/,MOD,SHR,SHL
5 +,-
6 EQ,NE,LT,LE,GT,GE
7 NOT
8 AND
(最低)9 OR,XOR
可以用圆括号改变运算的顺序
4.5 程序的段结构
8086/8088在管理内存时,按照逻辑段进行划分, 不同的逻辑段可以用来存放不同目的的数据。
在程序中使用四个段寄存器CS,DS,ES和SS来访问它们。在源程序设计时,使用伪指令来定义和使用这些逻辑段。
段定义伪指令
伪指令SEGMENT和ENDS用于定义一个逻辑段。使用时必须配对,分别表示定义的开始与结束
一般格式:
段名 SEGMENT [定位类型] [组合类型] [‘类别名']
…...
(本段语句序列)
…...
段名 ENDS
段定义伪指令语句各部分的作用如下:
段名:一个段的开始与结尾用的段名必须一致
定位类型:定位类型用于决定段的起始边界,即第一个可存放数据的位置(不是段基址)。
它可以有4种取值:
PAGE: 表示该段从一个页面的边界开始
PARA:表示该段从一个小节的边界开始
WORD:表示该段从一个偶数字节地址开始,即段起始单元地址的最后一位二进制数一定是0 BYTE:表示该段起始单元地址可以是任一地址值
组合类型:组合类型说明符用来指定段与段之间的连接关系和定位。 它有六种取值选择:
若未指定组合类型,表示本段与其它段无连接关系。在装入内存时,本段有自己的物理段,因此有自己的段基址
PUBLIC:在满足定位类型的前提下,将与该段同名的段邻接在一起,形成一个新的逻辑段,共用一个段基址。段内的所有偏移量调整为相对于新逻辑段的段基址
COMMON: 产生一个覆盖段。
STACK:把所有同名段连接成一个连续段,且系统自动对SS段寄存器初始化为该连续段的段基址。并初始化堆栈指针SP。
AT表达式:表示本段可定位在表达式所指示的小节边界上。表达式的值也就是段基值
MEMORY:表示本段在存储器中应定位在所有其它段之后的最高地址上。如果有多个用MEMORY说明的段,则只处理第一个用MEMORY说明的段。其余的被视为COMMON
类别名:类别名为某一个段或几个相同类型段设定的类型名称。系统在进行连接处理时,把类别名相同的段存放在相邻的存储区,但段的划分与使用仍按原来的设定
类别名必须用单引号引起来。所用字符串可任意选定, 但它不能使用程序中的标号、变量名或其它定义的符号
段寻址伪指令
段寻址伪指令ASSUME的作用是告诉汇编程序,在处理源程序时,定义的段与哪个寄存器关联。ASSUME并不设置各个段寄存器的具体内容,段寄存器的值是在程序运行时设定的
一般格式:ASSUME 段寄存器名:段名,段寄存器名:段名,......
在一个代码段中可以有几条ASSUME伪指令,对于前面的设置,可以用ASSUME改变原来的设置
可以使用关键字NOTHING将前面的设置删除
段寄存器的装入
段寄存器的初值(段基值)装入需要用程序的方法来实现。四个段寄存器的装入方法略有不同
DS和ES的装入:在程序中,使用数据传送语句来实现对DS和ES的装入
SS的装入
在段定义伪指令的组合类型项中,使用STACK参数,并在段寻址伪指令ASSUME语句中把该段与SS段寄存器关联
如果在段定义伪指令的组合类型中,未使用STACK参数,或者是在程序中要调换到另一个堆栈,这时,可以使用类似于DS和ES的装入方法
CS的装入
CPU在执行指令之前根据CS和IP的内容来从内存中提取指令,即必须在程序执行之前装入CS和IP的值。因此,CS和IP的初始值就不能用可执行语句来装入
装入CS和IP一般有下面两种情况:
由系统软件按照结束伪指令指定的地址装入初始的CS和IP。任何一个源程序都必须以END伪指令来结束。其格式为:END 起始地址
在程序运行期间,当执行某些指令时,CPU自动修改CS和IP,使它们指向新的代码段
4.6 过程定义伪指令
在程序设计过程中,常常将具有一定功能的程序段 设计成一个子程序。在MASM宏汇编程序中,用过程(PROCEDURE)来构造子程序
过程定义伪指令格式如下:
过程名 PROC [NEAR/FAR]
...... RET ......
过程名 ENDP
过程名是子程序的名称,它被用作过程调用指令CALL的目的操作数。它类同一个标号的作用。具有段、偏移量和距离三个属性。而距离属性使用NEAR和FAR来指定,若没有指 定,则隐含为NEAR
每一个过程中必须包含有返回指令RET,其作用是控制CPU从子程序中返回到调用该过程的主程序
4.7 当前位置计数器$与定位伪指令ORG(Origin)
汇编程序在汇编源程序时,每遇到一个逻辑段,就要为其设置一个位置计数器,它用来记录该逻辑段中定义的每一个数据或每一条指令在逻辑段中的相对位置
在源程序中,使用符号来表示位置计数器的当前值。因此,被称为当前计数器。它位于不同的位置具有不同的值
位置计数器在使用上完全类似变量的使用
定位伪指令ORG——用来改变位置计数器的值
格式:ORG 数值表达式
作用:将数值表达式的值赋给当前位置计数器$ 。ORG语句为其后的数据或指令设置起始偏移量
表达式的值必须为正值。表达式中也可以包含有当前位置计数器的现行值$
4.8 编题伪指令TITLE
语句格式:TITLE 标题名
作用:给所在程序指定一个标题。以便在列表文件的每一页的第一行都显示这个标题。其中标题是用户任意选用的字符串,字符个数不能超过60
4.9 从程序返回操作系统的方法
为了使程序运行结束后,能够正确地返回到操作系统,需要在程序中加上一些必要的语句。一般有以下两种方法:
使用程序段前缀PSP(Program Segment Prefix)实现返回
DOS系统将一个.EXE文件(可执行文件)装入内存时,在该文件的前面生成一个程序段前缀PSP,其长度为100H字节。同时让DS和ES都指向PSP的开始,而CS指向该程序的代码段,即第一条可执行指令
使用DOS系统功能调用实现返回
执行DOS功能调用4CH,也可以控制用户程序结束,并返回DOS操作系统