名词解释
1、指令系统:CPU能识别的所有指令的集合。
2、机器语言:指令代码语言。
3、机器语言程序:用机器语言编写的程序。
4、汇编语言:符号化语言。
5、汇编语言(源)程序:用汇编语言编写的程序。
6、汇编:将汇编语言源程序翻译成机器语言的过程。
7、反汇编:将机器语言程序翻译成汇编语言程序。
1 | MOV AL,12H //BO 12 |
//后为汇编后的结果。
文件名.ASM->文件名.OBJ(目标码文件)。
连接 Link.exe程序生成.exe文件
8086汇编语言程序中语句的种类
1、指令语句:CPU能执行的语句。汇编后能翻译成二进制指令代码的语句。
2、伪指令语句:CPU不能执行的语句。汇编后不能翻译成二进制指令代码的语句。如DB、DW。
3、宏指令语句:本身是8086指令系统没有的指令。是用户用宏定义伪指令定义的一条新语句。
汇编语言中语句的组成
名称、助记符、操作数、注释
注意:凡是数字,必须以0~9开头,凡是名称,必须以字母开头。
如:DB,means data byte,所定义的数据类型为一个字节宽度。
DW,means data word,所定义的数据类型为两个字节。
DD,means data double word,所定义的数据类型为四个字节。
变量名
标号名—next:(冒号不能少)
汇编语言程序设计中用到的常数及表达式
1、二进制常数以大写B结尾。
2、十进制常数D缺省。
3、十六进制常数以大写H结尾。
1 | MOV DL,'A'//A的阿思科码常数 |
标号、变量及伪指令
标号
1 | NEXT:....... |
1、标号一旦定义了,就具有三个属性:NEXT所在存储单元的段地址属性(SEG);段内偏移地址属性(OFFSET)较常用;类型(TYPE)分为NEAR和FAR两种类型。
NEAR—近程— -1(在同一个代码段内跳转)
FAR—远程— -2(不在同一个代码段内跳转)
变量
1、DB:定义字节型变量1
2、DW:定义字型变量2
3、DD:定义双字型变量4
例:在DS段有以下变量定义:
1 | DAT1 DB 12,12H,-12,'1' |
变量一旦定义了,就具有了五个属性:
1、段地址属性;SEG
2、段内偏移地址(有效地址)属性;OFFSET
3、类型的属性;(值为变量所占的字节数)TYPE
4、长度属性;LENGTH
5、大小属性。SIZE
获取DAT1的类型
如:MOV AL,TYPE DAT1
等效于:MOV AL,1
获取DAT1的段内偏移
MOV BX,OFFSET DAT1
BX是地址寄存器,才有进一步寻址的意义,否则给AX寄存器语法无误,但是没有现实意义。
一个逻辑段的地址,没有特别说明,段内偏移地址为0。
1 | DAT2 DW $+2,56ACH,78H |
在MASM.exe中有一个$位,叫做位置计数器(16位),表示汇编程序汇编到的位置。
1 | DAT3 DB 'THIS' |
长度:在变量名定义语句中,所定义的变量的个数。有DUP语句时,数字参数就是变量数,没有DUP的语句就是定义了一个变量。
大小:在变量名定义语句中,所定义的所有变量所占的总的字节数叫做该变量的大小。DAT9占了6个字节,则其大小为6个字节。
1 | MOV AL,DAT1+2// |
指令的分类
1、数据传送类指令。MOV。
2、算术运算类。加减乘除
3、逻辑运算指令。与、或、抑或、非
4、移位类
5、标志位操作指令
6、转移类指令。条件转移指令、无条件转移指令、子程序调用指令。
7、循环控制指令。LOOP
8、子程序调用与返回指令。call
9、中断调用返回指令。
10、字符串操作类指令。
11、输入输出指令。
12、其他指令。
13、宏指令。
目的操作数DST
操作数OPR
数据与转移地址的寻址方式
寻址方式
寻址方式就是求操作数所在地或者算在存储器单元地址的方式。
求得的操作数OPR可以作为操作或运算的数据用,或者用作实现转移的转移地址用。(段内转移求偏移地址给IP,段间转移还要求得目的地的段地址给CS)。
关于寻找数据的寻址方式
1、立即数寻址。要寻找的操作数指令中给出了,如
1 | MOV AX,1234H//目的操作数,源操作数(立即数寻址) |
说明立即数只能做源操作数。
2、寄存器寻址。要寻找的操作数在某寄存器中。如
1 | MOV AX,BX//源操作数的寻址方式是寄存器寻址 |
当源操作数和目的操作数都是寄存器寻址时,要注意类型要一致。
1 | MOV [0200H],56H//[]里是个地址,DS:0200H,立即数没有类型,该指令语法错误 |
在书写指令时,要注意类型要明确。源和目的操作数只要有一方明确,则类型明确。
1 | MOV DS,1500H//error! |
在给段地址寄存器(DS\ES\SS)赋值时,需要通过中间的通用寄存器。
当DS、ES、SS做目的操作数时,源操作数不能为立即数或立即数寻址。
1 | MOV CX,AX |
CX\IP不能做目的操作数。只能通过执行转移指令操作数改变CX和IP内的值。(IP指令指针寄存器)。
堆栈必须按照字操作。
3、存储器寻址。要寻找的操作数OPR在存储器某单元中。存储操作数的单元的EA(段内偏移地址)可以由以下五种寻址方式求得:
(一)直接寻址。在指令中直接写出操作数所在单元的地址。
(二)寄存器间接寻址。寄存器有基址寄存器BX、源变址寄存器SI、目的变址寄存器DI,形式如[SI]。
(三)寄存器相对寻址。基址寄存器BX、基址指针寄存器BP、源变址寄存器SI、目的变址寄存器DI,这四者之一加上一个8/16位的disp(相对位移量),两部分之和就是操作数所在存储单元的地址。
(四)基址、变址寻址。基址[BX],[BP]。变址寄存器[SI],[DI]。
(五)基址、变址且相对寻址。基址寄存器+变址寄存器+相对位移量。
直接寻址
操作数所在单元的有效地址指令中直接给出了。如:
1 | MOV AL [2000H] |
1 | //DS段 |
两存储器单元之间不能直接操作。
寄存器间接寻址
段内偏移只可能存储在寄存器[BX],[SI],[DI]中。段地址位于[DS]中。
1 | data1 DB 12H |
寄存器相对寻址
操作数所在存储单元的段内十六位偏移地址是由两部分相加。第一部分为[BX],[BP],[SI],[DI],第二部分为一个相对位移量disp。
当使用[BX],[SI],[DI]这三个寄存器中的任意一个时,段地址默认为DS寄存器。
当使用[BP]寄存器时,段地址默认为SS段。只有在寄存器相对寻址中才出现了BP,在寄存器间接寻址中没有BP参与。
1 | MOV [BP],AL; |
例:8086型号的CPU执行以下程序:
1 | PUSH AX |
基址变址寻址
操作数所在存储单元的段内十六位有效地址由两部分组成。第一部分是在[BX],[BP]基址寄存器中。另一部分在[SI],[DI]变址寄存器中。
段内地址所在寄存器以基址寄存器为准,若基址寄存器使用[BX],则段地址在DS中;若基址寄存器使用[BP],则段地址在SS中。
1 | data1 DB 12H |
基址变址且相对寻址
操作数所在单元的有效地址由三部分组成。第一部分位于基址寄存器中。第二部分位于变址寄存器中。第三部分为8位/16位的相对位移量。
段地址所在寄存器:若偏移量是常数,则以以基址寄存器为准,若基址寄存器使用[BX],则段地址在DS中;若基址寄存器使用[BP],则段地址在SS中。若偏移量是变量,段地址所在寄存器需要根据变量的定义位置来确定。
1 | data1 DB 12H |
隐含寻址
1 | PUSH AX |
关于转移地址的寻址方式
1、段内转移——只有IP发生了改变
1)直接寻址(相对寻址,会加上一个相对位移量)。转移指令中直接给出了转移目的地的地址。以标号的形式。
1 | L1:... |
指令结构:操作码(一个字节)+ 当前IP位置(下一条指令起始地址)的相对位移量。如果相对位移量在[-128,0]范围内(负向转移),则使用一个字节记录相对位移量。如果相对位移量在[0,32767]范围内(正相转移),则使用两个字节记录相对位移量。因为此时转移位置还没有经过编译,不知道具体的相对位移量是多少,只能全部按照两字节相对位移量处理。当负向转移的相对位移量较大时,也可以考虑使用两个字节来存储相对位移量。
当确定正向转移的偏移量不超过127时,为了节省空间,可以使用下面指令
1 | JMP SHORT L1 |
转移到目的地的IP = 当前IP(待执行的下一条指令的首地址)+disp(相对位移量)
8086指令系统中所有的条件转移指令,只能在段内转移。且转移范围为[-128,127]字节之间。如果转移地址偏移量超过了这个范围,则需要使用无条件转移指令实现二级跳。
8086指令系统中所有的条件转移指令,它的寻址方式是段内相对寻址。
例:8086CPU执行JZ L1指令时,IP = 0100H,disp = FDH,这条指令执行以后IP的值为多少呢?
当前IP = 0102H,再加上FDH(补码表示的位移量),得出的结果为00FFH。
2)段内间接寻址
1 | ;DS段 |
例:若(AL) = 1,则转到L1;若(AL) = 2,则转到L2;若(AL) = 3,则跳转到L3。转移相对位移量小于127字节。
通过观察发现三条跳转指令顺序排列,首地址与AL内数字n的关系为
1 | MOV BX,offset TAB |
数据传送指令
除SAHF、POPF(标志寄存器内指令出栈)指令外,其余传送类指令CPU执行后对6个状态标志均无影响。
语法规则如下:
1、立即数只能做源操作数。
2、类型要一致。
3、类型要明确。
4、当DS、ES、SS这三个段寄存器做目的时,源操作数不能为立即数。
CS、IP不能做目的。
5、两存储单元不能直接操作。
6、在指令语句中,不允许两个变量参加运算。
通用数据数据传送指令
1 | //把源操作数以某种方式指明的内容传送到目的地去 |
‘通用’的是指一定能用到。
取有效地址指令LEA
1 | LEA REG(16bit),MEM |
例子:
1 | MOV BX,offset data1 |
取地址指针指令LDS、LES
1 | LDS REG(16bit),SRC |
例:某数X(字)所在存储单元的地址指针已经在POINT双字单元中,将DX<-X。
1 | LDS BX,POINT |
标志传送指令
LAHF-将标志寄存器flag(PSW)低八位的内容传入AH寄存器。
SAHF-将AH寄存器的内容写入标志寄存器的低八位位置。修改了标志寄存器的内容。
在8086指令系统中没有专门的使标志寄存器清零或置一的指令,但可以使用堆栈操作改变标志寄存器内容,注意不能使用标志传送指令修改TF寄存器的内容,这是因为TF寄存器位于标志寄存器第八位,属于高八位的范围,而该方式只对低八位有效,所以无法改变。
关于标志寄存器的内容,点击这里
数据交换指令XCHG
XCHG OST,SRC
1、其中OST、SRC均不能为立即数
2、所有段寄存器均不能参加交换
3、上文中提到的六点语法规则
1 | XCHG AX,BX |
字节转换指令XLAT
格式:XLAT(查表)
无操作数,源和目的都是隐含寻址
功能:AL<-(DS:(BX)+(AL))
例:将0-9及A-FH数字->对应
1 | //在DS段 |
堆栈操作指令
1、PUSH SRC
要注意SRC不能为立即数
2、PUSH DST
3、PUSHF
4、POPF
以上堆栈操作指令都必修按字操作同时遵守上述六个规格。
1 | POP CS |
算术运算类指令
1、CPU只要执行运算类指令,执行结果就会影响状态标志。
2、段寄存器不能参加运算。
加法指令
1、不带进位位加法
ADD DST SRC
目的操作数DST<-(DST)+SRC,同时根据和设置六个状态标志。ZF、PF、SF、CF、OF、AF。
2、带进位位的加法
ADC DST SRC
目的操作数DST<-(DST)+SRC+CF,同时根据和设置六个状态标志。
3、增一指令
INC DST
目的操作数DST<-(DST)+1,同时根据结果设置除CF以外的五个状态标志。对CF无影响。
例:两个字型变量相加代码
1 | LEA SI,DVAR |
减法指令
1、不带借位位减法
SUB DST,SRC
DST<-(DST)-(SRC),并根据结果设置六个状态标志。
2、带借位位的减法
SBB DST,SRC
DST<-(DST)-(SRC)-CF,CF为借位标志,并根据结果设置六个状态标志。
只要有一方类型明确了,双方就都明确了。
3、减一指令
DEC DST
DST<-(DST)-1,并根据结果设置除CF以外的状态标志,对CF没影响。
例:若(AL) = FFH,CF = 0,则CPU执行:
1 | INC AL |
指令后,(AL) = ?,CF = ?
(AL) = 00H,CF = 0。
4、比较指令
CMP DST,SRC
(DST)-(SRC),结果不存储,只是根据结果设置六个状态标志。分别为CF、SF、ZF、PF、OF。
产生条件,使用比较指令来产生条件。
测试条件满足吗?如果满足则转到代码一,如果不满足则转到代码二。
1 | MOV AL,α |
根据单个条件:
CF-JC(有借位则转移)+标号—段内直接寻址、JNC(没有解位则转移)
SF-JS(结果为负则转移)+标号、JNS(结果不为负则转移)
ZF-JZ/JE(结果为0则转移)+标号、JNZ/JNE(结果不为零则转移)
PF-JP(结果低八位1的个数为偶数则转移)+标号、JNP(结果低八位1的个数不为偶数则转移)
OF-JO(结果溢出则转移)+标号、JNO(结果未溢出则转移)
若α、β为无符号数,则不存在大小的关系,只存在高低的关系。(相等、高、低)。CF
高 JA/JNBE
低 JB/JNAE(不高于也不等于)
相等JE
不相等JNE
小于等于 JBE/JNA
大于等于 JAE/JNB
若α、β为有符号数,则存在大小的关系。(相等、大、小)。SF XOR OF = 1,目的小于源
大:JG/JNLE
小:JL/JNGE
小于等于:JLE/JNG
大于等于:JGE/JNL
所以,在编写程序时首先要明确参与运算的数据是有符号的还是无符号的。
5、求负指令
NEG DST
DST<-0-(DST),并根据结果设置六个状态标志。
例子:在存储器BUFFER单元有一个16位的带符号数,求该数的绝对值,并把结果放回原数。
1、分析题目-提出解决问题的算法。
当x≥0时,它的绝对值是它本身。
当x<0时,它的绝对值是它的相反数。
2、画出程序流程。
开始框和结束框必须用椭圆框。矩形框是处理框。
两数比较(x和0比较),使用SF条件,如果SF = 1,则x小于零,对x求负,结果存入BUFFER单元;如果SF!=1,则x不为负,可以直接结束程序。
3、编写程序。
1 | //定义DS段,data word,DW类型为字型变量 |
乘法指令
加减运算是有符号还是无符号,由程序员来确定。例如,参与运算的是无符号的,则使用JB、JA指令;参与运算的是带符号的,则使用JL、JG指令。但是在乘除法中,有符号数和无符号数有各自的乘除法指令。
1、无符号乘法
MUL SRC
2、有符号数乘法
IMUL SRC
3、上述指令目的操作数为隐含寻址。隐含的操作数为被乘数,指令中给出的源操作数为乘数。SRC不能为立即数寻址。
4、乘法指令为字节型乘法还是字型的乘法,由源操作数来决定。
字节乘法:(DST)被乘数隐含在AL中,AX(AH:AL)<-(AL)SRC。八位乘八位,得到的结果为十六位。
字乘法:(DST)被乘数隐含在AX中,DX:AX<-(AX)SRC。十六位乘上十六位,结果表示为32位。
5、无符号乘法积的结果:不管是字乘还是字节乘,指令执行后只影响CF、OF两个标志。其余的状态标志没定义。
若OF = 0,CF = 0,说明高八位积无效。
若OF = 1,CF = 1,说明高八位有有效积。
6、有符号数乘法积的结果:
字节乘法:字节乘法:(DST)被乘数隐含在AL中,AX(AH:AL)<-(AL)SRC。指令执行后只影响CF、OF两个标志。
若OF = 0,CF = 0,说明AH中的积无效。AH中所存数据为AL中数据的符号的扩展。
字乘法:(DST)被乘数隐含在AX中,DX:AX<-(AX)SRC。
若OF = 0,CF = 0,说明DX中的积无效。AH中所存数据为AX中数据的符号的扩展。
若OF = 1,CF = 1,说明DX中有有效积。
除法指令
1、无符号数除法:DIV SRC
2、有符号数除法:IDIV SRC
3、SRC不能为立即数寻址。
4、乘法指令为字节型乘法还是字型的乘法,由源操作数来决定。
5、无符号字节型除法:(DST)被除数隐含在AX中,(AX)/(SRC)后,商放在AL,余数放在AH。
无符号字型除法:(DST)被除数隐含在DX:AX中,(DX:AX)/SRC,商放在了AX中,余数放在了DX中。
6、在有符号除法中,被除数为正,除数为正,则商为正,余数为正。若被除数为正,除数为负,则商为负,余数为正。若被除数为负,除数为正,则商为负,余数为负。若被除数为负,余数为负,则商为正,余数为负。
7、除法指令执行之后,对六个状态标志均无定义。
例:将存储器BUFFER1中的字节型无符号二进制数转换为十进制数。并将转换后的结果以分离BCD数形式存入BUFFER2以下单元。(个位数存在低地址单元)
思想:除十取余
1 | 在DS段定义 |
符号扩展指令
1、CBW
无操作数,隐含地将AL中的八位二进制数扩展到AX中(十六位AH:AL)。AH中的内容是AL符号位的扩展。
2、CWD
无操作数,隐含地将AX中的十六位数扩展成32位,存储在DX:AX中。其中DX中的内容为AX中符号位的扩展。
3、这些指令的存在是为了在某些算术运算中类型要保持一致。
例:求Y = ab+c-18。其中a、b、c三个变量都是字节型的。
1、注意y显然是十六位的,把ab的结果存放在datay单元。
2、首先在ds段定义这些变量。然后在cs段编写程序。
1 | //ds |
注意,可以使用EQU语句定义符号常量,如上述程序中的18,可以定义为cc。
1 | cc EQU 18 |
BCD数调整指令
+:分离BCD数、组合BCD数
-:分离BCD数、组合BCD数
:分离BCD数
/:分离BCD数,且是先调整后运算
+-先运算再调整
1、加法BCD数调整指令
组合BCD:DAA
分离BCD:AAA
都隐含的是对AL中的内容的调整,执行结果会正常设置六个状态标志。
例子:组合BDC数运算56+73
1 | MOV AL,56H |
例子:分离BCD数运算4+8
1 | MOV AL,04H |
这是因为分离BCD数相加结果十位最大为1,所以可以直接根据AF标志位来确定AH的内容。
大部分场合使用的是组合BCD数的运算。
分离BDC数加法参与运算的不论是数字还是字符‘1’,运算得到的结果都相同。所以该指令又称为阿思科码加法调整指令。
2、减法BCD数调整指令
组合BCD数:DAS 减六修正原则,超过九则减六
分离BCD数:AAS
例:组合BCD数31-87
1 | MOV AL,31H |
3、乘法BCD数调整指令
分离BCD数:AAM
例:分离BCD数7*8
1 | MOV AL,07H |
积的调整指令会调整SF、ZF标志。
4、除法BCD数的调整
AAD
1、只能是分离BCD数运算
2、先调整,再运算。
例:27/4BCD数除法运算
1 | MOV AX,0207H |
AAD指令执行会影响PF,ZF,SF三个标志,剩余的三个标志没定义。
逻辑运算类指令-五条
1、段寄存器不能参加运算。
与:AND DST,SRC;(DST)按位与(SRC),CF、OF标志自动清零。AF没定义。正常设置剩余的SF、ZF、PF标志。
或:OR DST,SRC;(DST)按位或(SRC),对标志位的设置与AND指令相同。
异或:XOR DST SRC;(DST)按位异或(SRC)。对标志位的设置与AND指令相同。
测试指令:TEST DST SRC;(DST)位对位与(SRC),根据与的结果甚至标志,并不存放结果,对标志位的设置与AND指令相同。
非:NOT DST;(DST)<-(DST)按位取反。对六个状态标志均无影响。
例:测BX中存储的十六位位数(从右向左编号)的位一和位二,当这两位同时为0时,AL内容置一。
与运算 0000 0000 0000 0110
若BX中的数据与该数与的结果为0,则ZF = 1。
同时应该注意到,题目要求在测试完BX中的数据后BX中的数据应该保持不变,应考虑使用TEST指令。
1 | TEST BX,0006H |
例:测BX中存储的十六位位数(从右向左编号)的位一和位二,当这两位只有一位为0时,AL内容置一。
与的结果有两种可能
1、0000 0000 0000 0100
2、0000 0000 0000 0010
测量结果的PF标志位,PF = 1,1的个数为偶数,PF = 0,1的个数为奇数。
1 | TEST BX,0006H |
例:将DX寄存器中的数据低七位取反,使用异或运算,低七位异或1,高九位异或0。
例:将CX寄存器中的数据高八位和低八位交换。
1 | XCHG CH,CL |
移位类指令(八条)
移位指令(四条)
把被移位的操作数看成无符号数,称为逻辑移位,把被移位的操作数看成有符号数,称为算数移位。
逻辑移位
左移:SHL DST,CNT
DST为目的操作数,CNT为移位次数。
移位也是一种运算,段寄存器先然不能参加计算,作为目的操作数来使用。
当CNT = 1时,源操作数部分可以直接写出。当CNT大于1时,移位次数要用CL给出,事先要把移位次数MOV到CL中。这一规则适用于所有八条移位类指令。
移出的最高位到了CF中,最低位自动补零。
在执行完移位指令之后,CPU根据执行结果设置除了AF以外的状态标志。
移位次数超过一位之后,OF标志就没有意义了。
右移:SHR DST,CNT
DST可以是字节型数据,也可以是字型数据。
算数移位
左移:SAL DST,CNT
算数左移和逻辑左移是一回事。
右移:SAR DST,CNT
带符号数最高位为符号位,右移过程中保持最高位不变。最低位移出去移到CF中。
例:5*12
1 | 方法一 |
循环移位(四条)
操作数可以是字节型的,也可以是字型的,最高位移入CF中,同时最高位移入最低位中。也可以最高位移入CF中,CF中原来的内容移入最低位中。
不带CF的循环移位
左移:ROL DST,CNT
右移:ROR DST,CNT
带CF的循环移位
左移:RCL DST,CNT
指令中的C指的是带CF
右移:RCR DST,CNT
CPU执行后,只影响CF和OF状态标志,对其余状态表示没定义。
例:若(BX) = 1011 0110B,CF = 1,(CL) = 3,则CPU执行RCR BX,CL指令后,(BX) = _H。
0000 0000 1011 0110
移位后为A016H。注意BX是十六位寄存器,虽然在本题中BX只给了8bit数据。
例:在DX:AX中存放了一个32bit数据m,求m*16
DX中有16bit数据,AX中有16bit数据。
1 | MOV CL,4 |