1…汇编语言与80X86计算机组织结构
汇编语言简介
- 首先机器语言是一种二进制语言,从属于硬件设备,一般随CPU的不同而不同。每一条语句都是二进制形式的代码。
- 汇编语言用助记符来替代机器语言的操作码。例如ADD,MOV,SUB和CALL等。
- 汇编语言同机器语言之间是一一对应的关系,一条汇编语言指令对应一条机器语言指令。C++和Java等高级语言与汇编语言及机器语言之间是一对多的关系,一条简单的C++语句会被扩展成多条汇编语言或者机器语言指令。
- 一般编译型语言先要编译成汇编语言,然后编译成机器语言。
- 汇编语言与计算机指令集是相对应的,可移植性较差。
80X86微处理器
- 处理器字长
CPU在单位时间内(一个时钟周期)能一次并行处理的二进制数的位数叫字长。
一般来讲,字长越大,运算速度越快,能够支持的寻址空间(内存)越大 - 处理器主频
CPU内核工作的时钟频率,即每秒的时钟周期数。
基于微处理器的计算机系统
硬件
典型的微型计算机硬件主要由中央处理机、存储器、系统总线、I/O接口电路和I/O设备组成。
中央处理机(CPU):是微型计算机的核心部件,芯片内集成了运算器、控制器和寄存器组,用来执行程序指令,完成所有的算术和逻辑运算及全机的控制工作。
存储器:是微型计算机的重要组成部件,用来存放程序和数据。微型计算机的存储器分为“主存”和“辅存”两类。
- 主存(内存),CPU可以通过总线直接存取,微型计算机的主存储器主要都是采用半导体存储器,按照读写方式的不同,分为只读存储器ROM和随机存取存储器RAM两种类型;
- 辅存(外存),如磁盘、磁带、光盘等,CPU通过I/O接口对其进行存取,它的容量比内存大很多,但存取信息的速度要比内存慢得多。一般程序(包括数据)是存放在外存中的,只有当运行时,才把它从外存传送到内存的某个区域,再由CPU控制执行。
总线:是指传送信息的一组公共导线,是计算机各功能部件之间进行信息传输的通道。CPU、存储器和I/O接口电路之间通过数据总线、地址总线和控制总线相连,这三组总线统称为系统总线。
- 数据总线是用来传送数据信息的。该总线是双向总线。数据总线的位数(也称宽度)决定了一次能够传送数据的位数。
- 地址总线是传送地址信息的。该总线是单向总线,用来输出CPU要访问的内存单元或I/O端口的地址。地址总线位数决定了CPU可以直接寻址的内存空间的大小,对于n条地址总线,可直接寻址的内存范围为2n。例如,8086的地址总线为20位,可寻址的最大内存空间为220B,即1MB。
- 控制总线是用来传送控制信息的。这组信号线比较复杂,有的是微处理器送往存储器和I/O接口的控制信号,如读写控制信号、中断响应信号等;有的是将外界的请求或联络信号送往微处理器,如中断请求信号、准备就绪信号等。
I/O接口电路:由于微机的外部设备种类繁多、工作原理各异,它们不能直接连到微机系统总线上实现与主机通信,必须经过中间电路再与系统相连,这部分电路被称为I/O接口电路。
- 主要完成数据缓冲、信号变换、以及与CPU联络等工作。
- 为便于主机访问外设,将I/O接口电路中每个寄存器统一编号,称为I/O端口地址或端口号。80x86的I/O地址空间为64KB,可寻址65536个不同的I/O地址,端口地址的范围是0000H~FFFFH。
I/O设备:是指微型计算机配备的输入输出设备,也称外围设备(简称外设),是微型计算机必不可少的组成部分。对外设的管理是汇编语言的重要应用之一。
软件
微型计算机的软件分为系统软件和应用软件两大类。
- 系统软件是面向所有用户的一类软件,通常包括:操作系统(DOS、Windows、Linux等)、语言翻译程序、诊断调试程序、I/O驱动程序等。系统软件的核心是操作系统,所有应用的程序都是在操作系统构筑的平台上运行的。
- 应用软件主要是指用户围绕某项应用编写的各种程序。
中央处理机
8086CPU从功能上可分为两部分,即总线接口部件BIU(Bus Interface Unit)和执行部件EU(Execution Unit)
总线接口部件(BIU)
由段寄存器、指令指针寄存器、地址加法器、指令队列和输入输出控制电路等组成。BIU是8086与系统总线的接口,负责CPU与存储器、I/O端口传送数据。
- 段寄存器:包括4个16位的段寄存器,代码段寄存器CS、数据段寄存器DS、附加段寄存器ES,堆栈段寄存器SS。
- 16位指令指针寄存器IP:用来存放下一条要执行指令在代码段中的偏移地址。
- 20位的地址加法器:用来形成20位的物理地址。
- 6字节的指令队列:用于存放从内存中取来的指令,按照先进先出的方式工作,并按顺序送到EU中执行。其操作遵循下列原则:
(1)每当指令队列缓冲器中存满一条指令后,EU就立即开始执行。
(2)指令队列缓冲器只要有2个空字节时,BIU就会自动把指令取到指令队列中,直到填满为止。
(3)在执行转移、调用或返回指令时,接下去要执行的指令不再是程序中紧接着排列的那条指令了,这样,指令队列中已经装入的指令就没用了,则要清除指令队列缓冲器,并要求BIU从新地址开始取指令填入指令队列缓冲器。 - 总线接口控制电路:将CPU的内部总线与系统总线相连,是CPU与内存单元或I/O端口交换数据的必经之路。
执行部件(EU)
由算术逻辑部件(ALU)、通用寄存器、标志寄存器和执行部件控制电路等组成,它负责指令的执行和数据的运算。
- 通用寄存器:包括4个16位的数据寄存器AX、BX、CX、DX和4个16位的指针与变址寄存器SI、DI与SP、BP。
- 标志寄存器(FR):它是一个16位的寄存器,用来反映CPU运算的状态特征和存放控制标志。
- 算术逻辑部件(ALU):用来完成8位或16位二进制算术和逻辑运算。
- 执行部件控制电路:负责从总线接口部件的指令队列缓冲器中取指令,并对指令进行译码,根据指令要求向执行部件内部个部分发出控制命令以完成各条指令的功能。
8086寄存器组
通用寄存器组
数据寄存器
数据寄存器共有4个:AX、BX、CX、DX,用来保存操作数或运算结果等信息。
- AX(Accumulator)寄存器称为累加器。使用频度最高:用于算术、逻辑运算以及与外设传送信息等。
- BX(Base Register)寄存器称为基址寄存器:常用于存放存储器地址。
- CX(Count Register)寄存器称为计数器:一般作为循环或串操作等指令中的隐含计数器。在位操作中,当移多位时,要用CL来指明移位的位数;
- DX(Data Register)寄存器称为数据寄存器:常用来存放双字数据的高16位,在进行乘、除运算时,它可作为默认的操作数参与运算,亦可存放外设端口地址。
变址寄存器
寄存器SI和DI称为变址寄存器(Index Register),主要用于存放某个存储单元的偏移地址
- SI是源变址寄存器,DI是目的变址寄存器
- SI和DI一般与数据段寄存器DS联用,用来确定数据段中某存储单元的地址。
- SI和DI都具有自动增量或减量的功能。
指针寄存器
寄存器BP和SP称为指针寄存器(Pointer Register),主要用于存放堆栈内存储单元的偏移量
- SP(Stack Pointer)为堆栈指针寄存器,用于存放当前堆栈段中栈顶的偏移地址;
- BP (Base Pointer)为基址指针寄存器,用于存放堆栈段中某一存储单元的偏移地址。
专用寄存器
指令指针寄存器 IP(Instruction Pointer)
它总是保存下一次将要从主存中取出指令的偏移地址。
IP与CS段寄存器联用,可以确定下一条要取的指令的物理地址,因此IP是很重要的控制寄存器,用于控制程序的执行流程。
在目标程序运行时,IP的内容由微处理器硬件自动设置,程序不能直接访问IP,但一些指令却可改变IP的值,如转移指令、子程序调用指令等。
标志寄存器 (FLAGS/PSW : Program Status Word)
进位标志CF(Carry Flag)
当运算结果的最高位(第7或15位)有进位(加法)或借位(减法)时,进位标志置1,即CF=1;否则CF=0。
使用场合:多字节运算,无符号数比较大小和移位操作时,用到该标志
零标志ZF(Zero Flag)
若运算结果为0,则ZF = 1,否则ZF = 0。
使用场合:需判断运算结果是否为0。
符号标志SF(Sign Flag)
运算结果最高位为1,则SF=1(结果为负);否则SF=0 (结果为正)。用于有符号数的运算。
奇偶标志PF(Parity Flag)
当运算结果中1的个数为零或偶数时,PF = 1;否则PF = 0。
注意:PF标志仅反映最低8位中“1”的个数是偶或奇,即使是进行16位字操作。
溢出标志OF(Overflow Flag)
若算术运算的结果有溢出,则OF=1;否则 OF=0。
方向标志DF(Direction Flag)
用于串操作指令中,控制地址的变化方向:
设置DF=0,串操作的存储器SI和DI地址自动增加,使串处理从低地址向高地址方向处理。
设置DF=1,串操作的存储器SI和DI地址自动减少,使串处理从高地址向低地址方向处理。
CLD指令复位方向标志:DF=0
STD指令置位方向标志:DF=1
中断允许标志IF(Interrupt-enable Flag)
用于控制外部可屏蔽中断是否可以被处理器响应:
设置IF=1,则允许中断;
设置IF=0,则禁止中断。
CLI指令复位中断标志:IF=0
STI指令置位中断标志:IF=1
陷阱标志TF(Trap Flag)
用于控制处理器是否进入单步操作方式:
设置TF=0,处理器正常工作;
设置TF=1,处理器单步执行指令。
单步执行指令——处理器在每条指令执行结束时,便产生一个编号为1的内部中断。这种内部中断称为单步中断,所以TF也称为单步标志。
段寄存器
8086CPU的4个16位的段寄存器分别称为:代码段寄存器CS,数据段寄存器DS,堆栈段寄存器SS,附加数据段寄存器ES。80386起增加了FS、GS两个段寄存器(附加的数据段)。
存储器
-
存储单元的地址和内容
存储单元地址 :8086系统中,为了标识和存取每一个存储单元,给每个存储单元规定一个编号,这就是存储单元地址。
存储单元的内容 :一个存储单元中存放的信息称为该存储单元的内容。 -
内存单元的地址和内容的概念及其关系:
若设M表示某内存单元的地址,那么M单元的内容可表示为(M)。
如果M单元存放着地址信息N,则地址N单元的内容则可表示为(N)=((M))。
- 段起始地址:段不能起于任意地址,而必须从任一小段(paragraph)的首地址开始。
小段:每16个字节为一小段,共有64K个小段
段的大小:0-64K 范围内的任意字节。 - 每个逻辑段最大可以占用64KB存储区。实际上,可以根据实际需要来确定段的大小,它可以是1B、100B或在64KB范围内的任意字节。
- 各段也允许重叠。所谓重叠只是指每个段区的大小允许根据实际需要来分配,而不一定要占用64KB的空间。
- 通常把存储单元的实际地址与其所在段的段地址之间的距离称为段内偏移,也可称为有效地址(EA:Effective Address)或偏移地址(Offset)等。
- 有了段地址和偏移地址,就能唯一地确定某一内存单元在存储器内的具体位置(物理地址,PA:Physical Address)。
- 存储单元的物理地址分为两部分:段地址和偏移量。
2…80X86的寻址方式
寻址的概念
汇编指令
- 汇编指令格式:操作码 [操作数1 [,操作数2 [,操作数3]]] [;注释]
- 在使用两个操作数时,操作数1为目的操作数,操作数2为源操作数,运算结果存在操作数1中。
操作数有三种来源:
- 操作数在指令中–>立即数操作数 MOV AL,9
- 操作数在寄存器中–>寄存器操作数 MOV AL,BL
- 操作数在内存(存储器)单元中–>内存(存储器)操作数 MOV AL,[2000H]
ps:指令中给出该内存单元的地址。用[]表示存储器操作数
与数据有关的寻址方式
1.立即寻址方式
-
操作数是常数,直接存放在指令中,紧跟在操作码之后,作为指令的一部分
-
立即数常用来给寄存器或内存单元赋初值
-
只能用于源操作数字段,不能用于目的操作数字段
2.寄存器寻址方式
-
指令所要的操作数已存储在某寄存器中,或把目标操作数存入寄存器
-
寄存器寻址方式的指令具有较快的执行速度,因为指令所需的操作数已存储在寄存器中,在指令执行过程中,会减少读/写存储器单元的次数
-
指令中可以引用的寄存器如下:
8位寄存器:AH、AL、BH、BL、CH、CL、DH、DL等;
16位寄存器:AX、BX、CX、DX、SI、DI、SP、BP和段寄存器等。
3.存储器寻址
-
在8086/8088里,将操作数的偏移地址又称为有效地址EA
-
双操作数指令中只能有一个使用存储器寻址方式
-
用SI、DI和BX等来提供有效地址,则其缺省的段寄存器为DS。物理地址:PA=16×DS+EA
-
用BP来来提供有效地址,则其缺省的段寄存器为SS。物理地址:PA=16×SS+EA
-
8086/8088指令中,EA=基址+变址+位移量
-
位移量(displacement):存放在指令中的一个8位或16位的数,但它不是立即数,而是一个地址;
-
基址(base):存放在基址寄存器(BX、BP)中的内容。通常用于指向数据段中数组或字符串的首地址。
-
变址(index):存放在变址寄存器(SI、DI)中的内容。通常用来指向数组中某个元素或字符串的某个字符。
eg:一维数组或者表格或者字符串:用基址或者位移量表示首地址,而用变址来表示数组,表格或者字符串中的某一个位置
eg:二维数组:用位移量指向整个二维数组的首地址,用基址指向某一行的首地址,变址指向这一行的某一个元素。
直接寻址
-
定义:指令所要的操作数存放在内存中,在指令中直接给出该操作数的有效地址,这种寻址方式为直接寻址方式。
-
在汇编语言程序中,一般不直接用数值表示偏移地址,而用符号代替数值表示地址,称符号地址(变量名)。
-
eg:符号buffer表示一个地址
MOV AX , [buffer] 或 MOV AX , buffer
源操作数为buffer指向的内存单元的内容 -
符号地址经汇编连接后,与一个确定的数值地址相对应。可用操作符Offset 获取变量的偏移地址。
PA = DS×10H + Offset buffer -
显式指定段跨越前缀:MOV AX, ES:[buffer]
寄存器间接寻址
- 定义:操作数的有效地址存储在基址寄存器或变址寄存器当中。可用的寄存器有 BX 、BP、SI、DI
寄存器相对寻址方式
-
定义:操作数在存储器中,其有效地址是一个基址寄存器(BX、BP)或变址寄存器(SI、DI)的内容和指令中的8位/16位偏移量之和。
-
汇编格式:X[R]或[R+X](X表示位移量,是8位或16位有符号数)
基址变址寻址方式
-
定义:操作数在存储器中,其有效地址是一个基址寄存器(BX、BP)和一个变址寄存器(SI、DI)的内容之和。
-
汇编格式: [BR+IR]
相对基址变址寻址方式
-
定义:操作数在存储器中,其有效地址是一个基址寄存器(BX、BP)的值、一个变址寄存器(SI、DI)的值和指令中的8位/16位位移量之和。
-
汇编格式:X[BR+IR] 或 [BR+IR+X]
总结
立即操作数:
立即寻址 MOV AX , 1010H
寄存器操作数:
寄存器寻址 MOV AX , BX
储存器操作数:
直接寻址 MOV AL , [buffer]
寄存器间接寻址 MOV AL , [BX]
寄存器相对寻址 MOV AL , [BX+10H]
基址变址寻址 MOV AL , [BX+SI]
相对基址变址寻址 MOV AL , [BX+SI+10H]
与转移地址有关的寻址方式
用来确定转移指令(条件转移指令或无条件转移指令)及call指令的转向地址。
转移地址是由各种寻址方式得到的有效地址和段地址相加而成的,有效地址存入IP寄存器中,段地址指定为CS段寄存器内容。
-
段内寻址:段内转移只需改变IP
段内直接寻址 JMP NEAR PTR NEXT
段内间接寻址 JMP TABLE [BX] -
段间寻址:段间转移改变CS、IP
段间直接寻址 JMP FAR PTR NEXT
段间间接寻址 JMP DWORD PTR [BX]
段内直接寻址:
-
转向的有效地址(IP+) = 当前(IP) + 位移量(8bit/16bit)
-
对于短转:8位位移量可正可负,范围是-128~+127,前面加“SHORT”
-
对于近转:16位位移量可正可负,范围是-32768~+32767,前面加“NEAR PTR”
-
段内直接寻址适用于条件转移及无条转移指令
-
当用于条件转移指令时,位移量只允许8位;
-
用于无条件转移指令时,位移量8位时称为短跳转,16位时则称为近跳转。
JMP SHORT QUEST ;8位短跳转
JMP NEAR PTR NEXT ;16位近跳转
其中NEXT、QUEST均为转向的符号地址,机器中用位移量表示
段内间接寻址
-
转向的有效地址是一个寄存器或存储单元的内容。可用除立即数以外的任何一种数据寻址方式得到,所得到的转向的有效地址取代IP寄存器的内容。
-
有效地址形成物理地址里的内容即为转向的有效地址(IP)。
转向物理地址的计算公式:PA = CS * 10H + IP -
汇编格式:JMP word ptr [BP + table]
word ptr 为属性定义符,表示BP + table寻址所得地址是一个字的有效地址,
段间直接寻址
- 用指令中提供的转向段地址和偏移地址取代CS和IP。
eg:JMP FAR PTR NEXTROUNT
offset NEXTROUNT->(IP)
seg NEXTROUNT->(CS)
ps:offset 取偏移地址,seg 取段地址
段间间接寻址
- 用存储器中的两个相继字的内容取代CS和IP,存储单元的地址可用存储器寻址方式得到。
eg:JMP DWORD PTR [INTERS+BX]
DWORD PTR——双字操作符,转向地址双字(段间转移),先IP、后CS
3…汇编语言程序格式
汇编程序功能
用汇编语言编写的程序能够直接利用硬件系统的特性,直接对位、字节、字寄存器、存储单元、I/O端口等进行处理,同时也能直接使用CPU指令系统和指令系统提供的各种寻址方式编制出高质量的程序,这种程序不但占用内存空间少,而且执行速度快 。
用汇编语言编写的源程序在输入计算机后,需要将其翻译成目标程序,计算机才能执行相应指令,这个翻译过程称为汇编,完成汇编任务的程序称为汇编程序。
汇编程序的主要功能:
(1)检查源程序,给出出错信息。
(2)产生目标文件(.obj)和列表文件(.lst)。
(3)展开宏指令。
汇编语言程序格式
语句是汇编语言源程序的基本组成单位。一个汇编语言源程序中有3种基本语句:指令语句、伪指令语句和宏指令语句(宏指令语句就是由若干条指令语句形成的语句)
1.指令语句
指令语句就是计算机中指令系统的各条指令,每条指令语句在汇编时都产生一个供CPU执行的机器目标代码。
-
计算机中每条指令语句表示一种基本功能,这些基本功能是在程序运行期间由计算机硬件来实现的。
-
一条指令语句由四个字段组成,其一般格式如下:
[标号名:] 操作码 [操作数] [;注释]
2.伪指令语句
伪指令属于汇编控制命令,它所指示的操作是由汇编程序在汇编源程序时完成的,在汇编时,它不产生目标代码,在将源程序汇编成目标程序后,它就不复存在了。
- 一条伪指令语句也由四个字段组成,其一般格式如下:
[符号名] 伪操作码 操作数 [;注释]
- 伪指令语句与指令语句的主要区别是:
1.伪指令语句经汇编后不产生机器目标代码,而指令语句经汇编后将产生相应的机器目标代码;
2.伪指令语句所指示的操作是在程序汇编时完成的,而指令语句的操作必须在程序运行时才能完成。
名字项(标号、符号名、变量等):
名字也就是由用户按一定规则定义的标识符。
名字可用下列字符组成:
- 英文字母(A ~ Z,a ~ z)
- 数字(0~9)
- 特殊字符(?、· 、@、—、$)
名字的定义要满足如下规则:
- 数字不能作名字项的第一个字符。
- 圆点(.)仅能用作第一个字符。
- 单独的“?”不能作为名字。
- 汇编语言中有特定含义的保留字,如操作码、寄存器名等,不能作为名字使用。
- 可以用很多字符来说明名字,但只有前面的31个字符能被汇编程序所识别。
标号:
标号用来代表一条指令所在单元的地址,在代码段中定义及使用。
- 标号放在语句的前面,并用冒号“:”与操作项分开。
- 标号不是每条指令所必需的,它也可以用LABEL或EQU伪指令来定义。
- 标号经常在转移指令或循环指令或CALL指令的操作数字段出现,用以-表示转向的目标地址。
变量:
变量在数据段、附加段和堆栈段中定义。它也可以用LABEL或EQU伪指令来定义。变量是一个可以存放数据的存储单元的名字,即存放数据的存储单元的地址符号名。
变量可以是用DB、DW、DD定义的字节、字或双字操作数,也可以被定义为一个数据区(有具体数值)或存储区(只定义存储区域,而不指定具体的数值)。
段属性、偏移属性及类型属性
标号和变量都具有三种属性:段属性、偏移属性及类型属性
- 段属性
标号或变量的段起始地址,此值必须在一个段寄存器中。 - 偏移属性
标号或变量所在的地址距段基址的偏移量。它们通常在指令中以显式方式出现,并最终能确定其有效地址EA。 - 类型属性
指出该标号是在本段内引用还是在其它段中引用。如在段内引用的,则称为NEAR,指针长度为2个字节;如在段外引用,则称为FAR,指针长度为4个字节。
变量的类型属性定义该变量所保留的字节数。如BYTE(1字节),WORD(2字节),DWORD(4字节),FWORD(6字节),QWORD(8字节),TBYTE(10字节)。
ps:在同一个程序中,同样的标号或变量的定义只允许出现一次
操作项:
操作项是由系统定义的,可以是指令、伪指令或宏指令的操作码,或称助记符。
- 对于指令:汇编程序将其翻译成机器语言指令。
- 对于伪指令:汇编程序将根据其所要求的功能进行处理。
- 对于宏指令:则将根据其定义展开。
操作数
操作数项是操作项的操作对象,可以是数据本身、标号、寄存器名字或算术表达式。
- 该项由一个或多个表达式组成,多个操作数项之间一般用逗号分开。
- 对于指令,操作数项一般给出操作数地址
- 对于伪指令或宏指令则给出它们所要求的参数。
- 操作数表达式是由运算量和运算符组成的表示操作数或操作数地址的运算式。
常量
常量是没有任何属性的纯数值,它的值在汇编期间已能完全确定,且在程序运行中也不会发生变化。
常量分为数值常量、字符串常量和符号常量,它主要用于指令语句中的立即数或伪指令语句中给变量赋初值等。
- 1.数值常量
基数后缀:h、q/o、d、b
如果整数常量后面没有后缀,就被认为是十进制的
以字母开头的十六进制常量前面必须加一个0 - 2.字符串常量
字符串常量是用单引号或双引号括起来的一个字符或多个字符。
字符串常量以单引号或双引号中各字符的ASCII码形式存储在内存中
使用时可在单引号内直接写字符序列,如‘12AB’,也可写字符的ASCII码,如31H,32H,41H,42H,
引号可以嵌套 - 3.符号常量
符号常量是指用EQU伪指令或赋值语句“=”定义过的符号名,可作操作数项或在表达式中使用。
伪指令
伪指令语句又称为说明性语句或管理语句。它不同于指令性语句,不是直接命令CPU去执行某一操作,而是命令汇编程序应当如何生成目标代码。
伪指令是汇编程序对源程序进行汇编时处理的操作,完成逻辑段的定义、存储模式定义、数据定义、存储器分配、指示程序开始结束等功能。
常用伪指令:
- 段定义伪操作
- 程序开始和结束伪操作
- 数据定义及存储器分配伪操作
- 表达式赋值伪操作
- 地址计数器与对准伪操作
- 基数控制伪操作
段定义伪指令
段定义伪指令的格式如下:
段名 SEGMENT [定位类型] [组合类型] [使用类型] [‘类别’]
……
……
段名 ENDS
SEGMENT和ENDS前边的段名表示定义的逻辑段的名字,必须相同
当定义除代码段以外其他段时,段内不能包括指令语句。
code_seg segment
mov ax, datas
start:
mov ds, ax
......
code_seg ends
end start
code_seg ends 表示代码段结束
End表示源程序结束。
start:一个标号,定义了程序的入口,程序从start处开始执行,
若程序的第一条指令就是程序的入口,则start可以缺省。
其中 start 可以用其他字符代替,但是对应的end start 中的start 也必须用同字符代替。
若第一个start缺省,则end start中的start也必须去掉。
1.定位类型:定位类型任选项告诉汇编程序如何确定逻辑段的边界在存储器中的位置
- PAGE:相应段必须从某一页的边界开始
XXXX XXXX XXXX 0000 0000B - PARA:相应段必须从某一个节(16个字节)的边界开始
XXXX XXXX XXXX XXXX 0000B - DWORD:相应段必须从任一个双字的边界开始
XXXX XXXX XXXX XXXX XX00B - WORD:相应段必须从任一个字的边界开始
XXXX XXXX XXXX XXXX XXX0B - BYTE:相应段可以从任一地址开始
XXXX XXXX XXXX XXXX XXXXB
定位类型的默认项为:PARA。
ps:
这里指的是逻辑段的起始单元地址,而逻辑段的段基地址必须从PARA的边界开始。
因此,对于起始单元为PAGE和PARA时,起始单元的有效地址一般为0, 而起始单元为DWORD,WORD和BYTE时,起始单元的有效地址不一定为0,因为段基地址和起始单元地址不一定指向同一个存储单元。
2.组合类型:在多模块程序设计中表示该段和其他同名段间的组合连接方法。
- PRIVATE(MEMORY):该段为私有段,在连接时不与其它模块中的同名段合并。(默认)
- PUBLIC:连接时,可把不同模块中的同名段连接而成一个段,连接的顺序由连接命令指定。
- COMMON:表示该段与别的模块中的同名同类别段共享相同的存储空间,所以会产生覆盖,其长度是各分段中的最大长度,重叠部分的内容取决于排在最后一段的内容。
- STACK:把不同模块中的同名段组合成一个堆栈段。长度为原有各段的总和。
3.使用类型:用来说明使用16位寻址方式还是32位寻址方式
只适用于386以后的机型
- USE16(默认)
- USE32
4.类别:
是程序员任选的一个字符串,使用时必须用单引号括起来,连接时将把不同模块中的相同类名的各段在物理地址上相邻的连接在一起(并不合并),其顺序则与LINK时提供的各模块相同。如代码段(‘CODE’)、堆栈段(‘STACK’)等。
ASSUME伪指令
段定义后,还要规定段的性质,明确段和段寄存器的关系,这可用ASSUME伪指令来实现。
ASSUME伪指令的格式为:
ASSUME 段寄存器:段名[,段寄存器:段名…… ]
ASSUME 段寄存器:NOTHING ;取消原段寄存器的指定段寄存器可以是CS、DS、ES、SS。
ASSUME伪指令只是指出各逻辑段应该装填的地址,但并未真正将段基址装入相应的段寄存器中,所以在程序的代码段开始处就应该先进行DS、ES、SS段基址的装填,否则无法正确对数据进行寻址操作。
ASSUME的个人理解:确定了标号的默认前缀,如ss:DATA,则DATA段中的标号都默认ss
CS由系统自动装填。堆栈段SS也可以不用用户装填,而由系统自动装填。但是在定义堆栈段时,必须把参数写全。其格式为:
STACK SEGMENT PARA PUBLIC ‘STACK’
eg:
data segment ; 定义数据段
…
data ends
code segment ; 定义代码段
assume cs:code, ds:data
start:
mov ax, data ;段名将被编译为一个表示段地址的数值而立即数不能直接送段寄存器
mov ds, ax ; 数据段段地址->段寄存器
code ends
end start
简化的段定义伪指令
- .CODE [name] ;代码段
- .DATA ;初始化数据段
- .DATA? ;未初始化数据段
- .FARDATA [name] ;远(调用)初始化数据段
- .FARDATA? [name] ;远(调用)未初始化数据段
- .CONST ;常数段
- .STACK [size] ;堆栈段
.MODEL SMALL ;使用简化段定义时,必须有存储模式.MODEL语句,且位于所有简化段定义语句之前。
.DATA
;此处输入数据段代码
.STACK
;此处输入堆栈段代码
.CODE
START:
MOV AX, @DATA ;用预定义符号@DATA给出了数据段的段名
MOV DS, AX
;此处输入代码段代码
END START
ps:
简化段定义只能采用系统默认的属性。
简化的段定义ASSUME伪指令可以省掉(如果只有.Data, .STACK, .CODE)
MODEL伪指令
格式:
.MODEL 存储模式 [,语言类型] [,操作系统类型] [,堆栈选项]
- 语言类型:C、BASIC、PASCAL等(被高级语言调用时)
- 操作系统:OS_DOS(默认)或OS_OS2
- 堆栈选项:NEARSTACK或FARSTACK(其中NEARSTACK是指把堆栈段和数据段组合到一个DGROUP中,即DS,SS均指向DGROUP段;FARSTACK指堆栈段和数据段不合并)
存储模式类型:
- TINY(微型模式)COM类型程序,只有一个小于64KB的逻辑段
- SMALL(小型模式)小应用程序,只有一个代码段和一个数据段(含堆栈段),每段不大于64KB
- COMPACT(紧凑模式)代码少、数据多的程序,只有一个代码段,但有多个数据段
- MEDIUM(中型模式)代码多、数据少的程序,可有多个代码段,只有一个数据段
- LARGE(大型模式)大应用程序,可有多个代码段和多个数据段(静态数据小于64KB)
- HUGE(巨型模式)更大应用程序,可有多个代码段和多个数据段(对静态数据没有限制,可超过64KB)
- FLAT(平展模式)32位应用程序,运行在32位80x86CPU和Windows 9x或NT环境
ps:
存储模式为Tiny, Small, Medium, Flat时,默认项为NEARSTACK
存储模式为Compact, Large, Huge时,默认项为FARSTACK
段组定义伪操作GROUP
使多个段共用一个段寄存器
格式
Groupname Group segname1, segname2…
数据定义及存储器分配伪操作
格式:
[变量] 助记符 操作数 [,操作数,…] [ ; 注释]
助记符:DB DW DD DF DQ DT
变量指向第一个字节的偏移地址
eg:
DATA_BYTE DB 10,4,10H,? ;?只表示分配存储空间并不存入确定的值,形成未初始化数据
复制操作符DUP
格式
Repeat_count DUP (操作数1, 操作数2, … ) [ ; 注释]
与前面的数据定义操作符联合应用
eg
ARRAY1 DB 2 DUP(0,1,2,?)
ARRAY2 DB 2 DUP(0,2 DUP(1,2),0,3)
即指针PTR
指定操作数的类型属性
格式
type PTR 变量或常量
type可以是BYTE,WORD,DWORD,FWORD,QWORD,TBYTE等等
eg
OPER1 DB 1, 2
MOV AX, OPER1+1 ;类型不匹配,AX为字类型,OPER1为字节指针
MOV AX, WORD PTR OPER1+1 ;类型匹配
LABEL
用LABEL来定义操作数类型
格式
name LABEL type
type可以是BYTE,WORD,DWORD,FWORD,QWORD,TBYTE等等。
表达式赋值伪操作EQU
EQU伪指令的功能是给各种形式的表达式赋予一个名字。在以后的程序语句中,均可用它的名字来代替该表达式。
格式:
表达式名 EQU 表达式
表达式可以是任何有效的操作数格式,可以是任何可以求出常数值的表达式,也可以是任何有效的助记符。
功能与“=”相似,但EQU不允许重复定义,而 “ = ” 伪操作允许重复定义
地址计数器与对准伪操作
地址计数器$:保存当前正在汇编的指令的偏移地址。每处理一条指令,$就增加一个值(此值为该指令所需要的字节数)。
- 当$用在指令中时,它表示本条指令的第一个字节的地址
- $用在伪操作的参数字段时,表示地址计数器的当前值
数值表达式ORG
ORG伪指令用来设置当前地址计数器的值。其功能是告诉汇编程序,其后的指令或数据从数值表达式所指定的偏移地址开始存放。直到遇到下一个ORG命令。
表达式的值在0000H~FFFFH(0~65535)之间。
eg
SEG1 SEGMENT ;VAR1的偏移地址是10
ORG 10
VAR1 DW 1234H
ORG 20
VAR2 DW 5678H
ORG $+8 ;建立一个8字节的未初始化的数据缓冲区
VAR3 DW 1357H
SEG1 ENDS
基数控制伪操作.RADIX
汇编语言默认的数是十进制,.RADIX伪指令可以把默认的基数改变为2~16范围内的任何基数。
格式:
. RADIX 表达式
在用“.RADIX 16”后,十进制后面的数都应该加D。如果某个16进制的末尾字符为D,则应在其后跟字母H,以免产生混淆.
表达式操作符
表达式是常数、寄存器、标号、变量与一些操作符组合的序列,可以有数字表达式和地址表达式两种
算术操作符: + - * \ Mod
算术操作符可以用于数字表达式或地址表达式,但用于地址表达式时,只有其结果有明确的物理意义时才是有效的结果。例如:地址+或-是有意义的,但两个地址*或/是无意义的
关系操作符: EQ、NE、LT、LE、GT、GE
计算结果为逻辑值:
真 0FFFFH
假 0000H
逻辑和移位操作符: AND、OR、XOR、NOT
数值回送操作符:TYPE、LENGTH、SIZE、 OFFSET、SEG
格式
操作符 变量
- TYPE:返回变量以字节数表示的类型
DB | DW | DD | DF | DQ | DT | NEAR | FAR | 常数 |
---|---|---|---|---|---|---|---|---|
1 | 2 | 4 | 6 | 8 | 10 | -1 | -2 | 0 |
- LENGTH:对于变量中使用DUP的情况,返回分配给该变量的单元数,其他情况返回1.
- SIZE:返回分配给该变量的字节数,等于TYPE值*LENGTH值。
- OFFSET:回送变量或标号的偏移地址
- SEG:回送变量或标号的段地址
属性操作符: SHORT、 HIGH、LOW、HIGHWORD、LOWWORD
- SHORT:用来修饰跳转指令中转向地址的属性,指出转向地址在下一条指令的-127~+127个字节范围之内。
- HIGH和LOW :字节分离操作符。对一个数或表达式,HIGH取其高字节,LOW取其低字节。
- HIGHWORD、LOWWORD:字分离操作符。对一个数或表达式,HIGHWORD取其高位字,LOWWORD取其低位字。
运算符的优先级别
8086/8088的指令系统
概述
操作码:指明CPU要执行什么样的操作。
操作数:指明参与操作的数据或数据所在的地方。
操作码按功能指令分六类:
- 数据传送
- 算术运算
- 逻辑运算
- 串操作
- 控制转移
- 处理机控制
操作数三种来源:
- 立即数操作数
- 寄存器操作数
- 存储器操作数(内存操作数)
操作数个数的多少分为四类:
- 无操作数: 指令只有一个操作码,无操作数:
1.有些操作不需要操作数。
2.操作数隐含在指令中。 - 单操作数: 指令中给出一个操作数:
1.有些操作只需要一个操作数
2.有些操作将另一个操作数隐含在指令中 - 双操作数: 指令中给出两个操作数:
目的操作数+源操作数 - 三操作数: 指令中给出三个操作数
目的操作数+源操作数*2
操作数类型
- 有的操作既可对字节操作,又可对字操作
有的操作只允许对字操作 - 指令应指明参与操作的数是字节还是字,即操作数的类型。
- 通常操作数的类型可由操作数本身隐含给出。在特殊情况下需要指明。
- 指令中有寄存器操作数,由寄存器操作数决定类型
- 指令操作数中无寄存器,则由内存操作数的类型决定
- 指令中无类型的依据,需对存储器操作数加类型说明
- 定长指令码格式:立即寻址最快,因为指令地址码即为操作数。
- 变长指令码格式:因为立即寻址操作数可能很长,取指令时可能需要两次访存。而寄存器寻址取指令只需一次访存,所以寄存器寻址最快。
- 操作速度:
寄存器操作数>立即数操作数>存储器操作数
通用数据传送指令
传送指令:MOV DST, SRC
执行操作:(DST) <- (SRC)
格式:
MOV mem/reg1 , mem/reg2 ;不能都用储存器,否则指令过长,解码过于复杂
MOV reg , data
MOV ac , mem ;ac是累加器
MOV mem , ac
MOV segreg , mem/reg ; 段寄存器不能为CS
MOV mem/reg , segreg
MOV mem/reg , data
不影响标志位
指令指针IP,不能作为MOV指令的操作数
在目标程序运行时,IP的内容由微处理器硬件自动设置,程序不能直接访问IP,但一些指令却可改变IP的值,如转移指令、子程序调用指令等。
进栈指令:PUSH SRC
执行操作:(SP) <- (SP)–2 , ((SP)+1,(SP)) <- (SRC)
格式:
PUSH reg
PUSH mem
PUSH segreg
8086不允许push指令使用立即数寻址
出栈指令:POP DST
执行操作:(DST) <- ((SP)+1,(SP)) , (SP) <- (SP)+2
格式
POP reg
POP mem
POP segreg ;(no CS)
- 堆栈:‘先进后出’的存储区,段地址存放在SS中,SP在任何时候都指向栈顶,进出栈后自动修改SP。
- 注意:
堆栈操作必须以字为单位。
不影响标志位
POP不能用立即寻址方式
POP指令DST不能是CS
所有寄存器进栈指令:PUSHA
功能:16位通用寄存器依次进栈,次序为AX、CX、DX、BX,指令执行前的SP、BP、SI、DI。指令执行后(SP)-16→(SP)仍指向栈顶。
格式
PUSHA
不影响标志位。
所有寄存器出栈指令:POPA
功能:16位通用寄存器依次出栈,次序为DI、SI、BP、SP,指令执行前的BX、DX、CX、AX。指令执行后(SP)+16→(SP) 仍指向栈顶。
格式
POPA
SP出栈只是修改了指针使其后的BX能够出栈,而堆栈中原先由PUSHA指令存入的SP的原始内容被丢弃,并未真正送到SP寄存器中。
不影响标志位。
交换指令:XCHG OPR1, OPR2
执行操作:(OPR1) <–> (OPR2)
格式
XCHG mem/reg , mem/reg ; 至少一个为寄存器
不影响标志位
换码指令:XLAT 或 XLAT OPR
执行操作:(AL) <- ((BX)+(AL))
也叫表格查询转译指令,用于两种不同编码之间的映射转换。通过转译索引表格来实现。
表格的首地址->(BX)
需转换的代码相对于首地址的位移量->(AL)
指令执行完后,AL中即为转换后的代码。
OPR只是为了增加可读性,首地址需要预先送到BX
不影响标志位
字节表格长度不能超过256个字节
有效地址送寄存器指令
有效地址送寄存器指令:LEA REG, SRC
执行操作:(REG) <- SRC的有效地址
相同功能。MOV指令比LEA更快,但是OFFSET只能与简单的符号地址相连,而LEA适用范围更广,可以与更复杂的寻址方式相连。
指针送寄存器
- DS指令:LDS REG, SRC
执行操作:(REG) <- (SRC) ,(DS) <- (SRC+2) - ES指令:LES REG, SRC
执行操作:(REG) <- (SRC) ,(ES) <- (SRC+2)
不影响标志位
REG 不能是段寄存器
SRC 必须为存储器寻址方式
标志寄存器传送指令
- 标志送AH指令:LAHF(Load AH with Flags)
执行操作:(AH) <- (FLAGS的低字节) - AH送标志寄存器指令:SAHF(Save AH into Flags)
执行操作:(FLAGS的低字节) <- (AH) - 标志进栈指令:PUSHF
执行操作:(SP) <- (SP)-2 , ((SP)+1,(SP)) <- (FLAGS) - 标志出栈指令:POPF
执行操作:(FLAGS) <- ((SP)+1,(SP)),(SP) <- (SP)+2
SAHF,POPF影响标志位
类型转换指令
- CBW
执行操作:AL中的内容符号扩展到AX - CWD
执行操作:AX中的内容符号扩展到DX
无操作数指令
隐含对AL 或AX 进行符号扩展
不影响条件标志位
算术指令
加法指令
加法指令:ADD DST, SRC
执行操作:(DST) <- (SRC)+(DST)
带进位加法指令:ADC DST, SRC
执行操作:(DST) <- (SRC)+(DST)+CF
加1指令:INC OPR
执行操作:(OPR) <- (OPR)+1
加法指令对条件标志位的影响:
1 | 0 | |
---|---|---|
SF | 结果为负 | 否则 |
ZF | 结果为0 | 否则 |
CF | 和的最高有效位有向高位的进位 | 否则 |
OF | 两个操作数符号相同,而结果符号与之相反 | 否则 |
减法指令
减法指令 SUB DST, SRC
执行操作 (DST) <- (DST) - (SRC)
带借位减法指令: SBB DST, SRC
执行操作: (DST) <- (DST) - (SRC) - CF
减1指令: DEC OPR
执行操作: (OPR) <- (OPR) - 1
求补指令: NEG OPR
执行操作: (OPR) <- 0FFFFH - (OPR)+1
比较指令: CMP OPR1, OPR2
执行操作: (OPR1) - (OPR2),不保存结果,只是根据结果设置标志位。
减法指令对条件标志位的影响
1 | 0 | |
---|---|---|
SF | 结果为负 | 否则 |
ZF | 结果为0 | 否则 |
CF | 被减数的最高有效位有向高位的借位 | 否则 |
OF | 两个操作数符号相反,而结果的符号与减数相同 | 否则 |
- DEC指令不影响CF标志
NEG 指令对CF/OF的影响
0 | 1 | |
---|---|---|
CF | 操作数为0 | 否则 |
OF | 否则 | 操作数为-128(字节运算)或-32768(字运算) |
0FFFFH-操作数+1 等效于 0-操作数,即用0减去操作数,只有操作数为0时,不需要借位。
乘法指令
无符号数乘法指令:MUL SRC
带符号数乘法指令:IMUL SRC
执行操作:
字节操作数 (AX) <- (AL)(SRC)
字操作数 (DX,AX) <- (AX)(SRC)
CF OF | 00 | 11 |
---|---|---|
MUL | 乘积的高一半为零 | 否则 |
IMUL | 乘积的高一半是低一半的符号扩展 | 否则 |
AL(AX) 为隐含的乘数寄存器。
AX(DX,AX) 为隐含的乘积寄存器。
SRC不能为立即数。
除CF和OF外,对条件标志位无定义。
除法指令
无符号数除法指令: DIV SRC
带符号数除法指令: IDIV SRC(余数的符号和被除数相同)
执行操作:
字节操作 (AL) <- (AX)/(SRC)的商
(AH) <- (AX)/(SRC)的余数
字操作 (AX) <- (DX,AX)/(SRC)的商
(DX) <- (DX,AX)/(SRC)的余数
AX (DX,AX) 为隐含的被除数寄存器。
AL (AX) 为隐含的商寄存器。
AH (DX) 为隐含的余数寄存器。
SRC不能为立即数。
对所有条件标志位均无定义
只要字节除法操作AH绝对值大于等于除数的绝对值、或者字除法操作DX的绝对值大于等于除数绝对值,都会发生除法溢出
逻辑指令
逻辑运算指令
逻辑非指令:NOT OPR(OPR不能为立即数)
执行操作: (OPR) <- (OPR)取反(不影响标志位)
逻辑与指令:AND DST, SRC
执行操作: (DST) <- (DST) & (SRC)
逻辑或指令:OR DST, SRC
执行操作: (DST) <- (DST) | (SRC)
异或指令: XOR DST, SRC
执行操作: (DST) <- (DST) ^ (SRC)
测试指令: TEST OPR1, OPR2
执行操作: (OPR1) & (OPR2)
与、或、异或、测试指令对标志位的影响
CF | OF | SF | ZF | PF |
---|---|---|---|---|
0 | 0 | 根据结果设置 | 根据结果设置 | 根据结果设置 |
移位指令和循环移位指令
逻辑左移 SHL OPR, CNT
逻辑右移 SHR OPR, CNT
算术左移 SAL OPR, CNT(同逻辑左移)
算术右移 SAR OPR, CNT
循环左移 ROL OPR, CNT
循环右移 ROR OPR, CNT
带进位循环左移 RCL OPR, CNT
带进位循环右移 RCR OPR, CNT
OPR可用除立即数以外的任何寻址方式
CNT=1,SHL OPR, 1
CNT>1,MOV CL, CNT
SHL OPR, CL ;以SHL为例
条件标志位:
CF | OF | SF | ZF | PF |
---|---|---|---|---|
移入的数值 | 最高有效位的值是(1)否(0)发生变化(cnt=1否则无定义) | 根据结果设置 | 根据结果设置 | 根据结果设置 |
循环移位指令:不影响 SF、ZF、PF、AF
移位指令可以用来作乘2或除2的操作,其中算术移位指令用于带符号数运算,逻辑移位用于无符号数运算。
串处理指令
- 设置方向标志指令
CLD : DF <- 0
STD : DF <- 1 - 串处理指令
串传送:MOVS
存入串:STOS
取出串:LODS
串比较:CMPS
串扫描:SCAS - 串重复前缀
重复:REP (重复操作直到计数器CX的值为0为止)
相等/为零则重复:REPE/REPZ
不相等/不为零则重复:REPNE/REPNZ
MOVS 串传送指令
MOVS DST, SRC (在操作数明确是字节、还是字传送)
- MOVSB (字节)
- MOVSW (字)
执行操作:
源串(默认数据段,可用段前缀修改)-> 目的串(必须附加段)
eg
MOVS ES: BYTE PTR [DI], DS: [SI]
方向标志:
DF=0 时从前往后(+)
DF=1 时从后向前(-)
执行 REP MOVS 之前,应先做好:
1.源串首地址(或末地址)-> SI
2.目的串首地址(或末地址)-> DI
3.串长度 -> CX
4.建立方向标志。
STOS 存入串指令
STOS DST (这里的DST只负责告知操作是字还是字节)
- STOSB (字节)
- STOSW (字)
执行操作:
字节操作:((DI))←(AL), (DI)←(DI)±1
字操作:((DI))←(AX), (DI)←(DI)±2
LODS 从串取指令:
LODS SRC
- LODSB (字节)
- LODSW (字)
执行操作:
字节操作:(AL)←((SI)), (SI)←(SI)±1
字操作:(AX)←((SI)), (SI)←(SI)±2
源串一般在数据段中(允许使用段跨越前缀来修改)
不影响条件标志位
LODS 指令一般不与 REP 联用,有时缓冲区中的一串字符需要依次取出来测试的时候,可以循环指令连用来实现此功能。
与REPE/REPZ/REPNE/REPNZ配合工作的CMPS 和 SCAS
REPE/REPZ(相等/为零则重复)
执行操作:
(1) 如 (CX)=0 或 ZF=0(即某次比较的结果两个操作数不相等)则退出串操作,否则转(2)
(2) (CX)←(CX) -1
(3) 执行 CMPS / SCAS ,回到(1)
实际上REPE和REPZ是完全相同的。
CMPS 串比较指令
CMPS SRC, DST
- CMPSB (字节)
- CMPSW (字)
字节操作:(SI)←(SI)±1, (DI)←(DI)±1
字操作: (SI)←(SI)±2, (DI)←(DI)±2
执行操作:
((SI)) - ((DI))
根据比较结果置条件标志位:相等 ZF=1;不等 ZF=0
SCAS 串扫描指令
SCAS DST
- SCASB (字节)
- SCASW (字)
字节操作:(AL) - ((DI)), (DI)←(DI)±1
字操作: (AX) - ((DI)), (DI)←(DI)±2
执行操作:
把AL/AX的内容与由目的变址寄存器指向的在附加段中的一个字节或字进行比较。
处理机控制与杂项操作指令
标志处理指令
CLC 进位位置0指令 ;CF ← 0
STC 进位位置1指令 ;CF ← 1
CMC 进位位求反指令 ;CF ← Not(CF)
CLD 方向标志位置0指令 ;DF ← 0
STD 方向标志位置1指令 ;DF ← 1
CLI 中断标志位置0指令 ;IF ← 0
STI 中断标志位置1指令 ;IF ← 1
以上指令只影响本指令指定的标志
CPU控制类指令
NOP 空操作指令
该指令不执行任何操作,但占用一个字节存储单元,空耗一个指令执行周期,常用于程序调试。
例如:在需要预留指令空间时用NOP填充,代码空间多余时也可以用NOP填充,还可以用NOP实现软件延时。
HLT 暂停指令
在等待中断信号时,该指令使CPU处于暂停工作状态,CS:IP指向下一条待执行的指令。当产生了中断信号,CPU把CS和IP压栈,并转入中断处理程序。在中断处理程序执行完后,中断返回指令IRET弹出IP和CS,并唤醒CPU执行下条指令。
4…循环与分枝程序设计
控制转移指令
无条件转移指令
-
段内直接短转移:JMP SHORT OPR
执行操作:
(IP) <- (IP) + 8位位移量 -
段内直接近转移:JMP NEAR PTR OPR
执行操作:
(IP) <- (IP) + 16位位移量 -
段内间接转移: JMP WORD PTR OPR
执行操作:
(IP) <- (EA) -
段间直接远转移:JMP FAR PTR OPR
执行操作:
(IP) <- OPR 的段内偏移地址
(CS) <- OPR 所在段的段地址 -
段间间接转移: JMP DWORD PTR OPR
执行操作:
(IP) <- (EA)
(CS) <- (EA+2)
条件转移指令:
只能使用段内直接寻址的8 位位移量(386以后机型支持16位位移量)
根据单个条件标志的设置情况转移
指令 | 转移条件 | |
---|---|---|
JZ | ZF=1 | |
JNZ | ZF=0 | |
JE | ZF=1 | equal |
JNE | ZF=0 | not equal |
JS | SF=1 | |
JNS | SF=0 | |
JO | OF=1 | |
JNO | OF=0 | |
JP | PF=1 | |
JNP | PF=0 | |
JPE | PF=1 | even |
JPO | PF=0 | odd |
JCXZ | CX=0 | cx equal zero |
无符号数比较
适用于地址或双精度数低位字的比较
指令 | 转移条件 | 效果 | |
---|---|---|---|
JB(JNAE) | CF=1 | < | below(not above equal) |
JNB(JAE) | CF=0 | >= | not below(above equal) |
JBE(JNA) | CF&ZF=1 | <= | below equal(not above) |
JNBE(JA) | CF&ZF=0 | > | not below equal(above) |
带符号数比较
适用于带符号数的比较
指令 | 转移条件 | 效果 | |
---|---|---|---|
JL(JNGE) | SF^OF = 1 | < | less(not greater equal) |
JNL(JGE) | SF^OF = 0 | >= | not less(greater equal) |
JLE(JNG) | (SF^OF)&ZF = 1 | <= | less queal(not greater) |
JNLE(JG) | (SF^OF)&ZF = 0 | > | not less equal(greater) |
循环指令
LOOP
语句格式:LOOP 短标号
执行过程:
(CX)=(CX)-1(不改变任何标志位)
如果CX!=0,则程序转到循环体的第一条指令;否则退出
LOOPE/LOOPZ
相等/为零循环指令
语句格式: LOOPE/ LOOPZ 短标号
执行过程:
(CX)=(CX)-1(不改变任何标志位)
如果CX!=0且ZF=1,则程序转到循环体的第一条指令;否则退出
LOOPNE/LOOPNZ
不相等/不为零循环指令
语句格式:LOOPNE/LOOPNZ 短标号
执行过程:
(CX)=(CX)-1(不改变任何标志位)
如果CX!=0且ZF=0,则程序转到循环体的第一条指令;否则退出
循环结构程序设计
循环初始化部分
循环体
循环修改部分
循环控制部分
Loop指令会自动修改CX的值,多重循环的时候要注意CX的保存和恢复
分支结构程序设计
if-then-else结构:
cmp AX, BX
JE ElseCode
<THEN 程序段>
jmp EndOfIF
ElseCode: <ELSE 程序段>
EndOfIF:
switch结构:
cmp AL, 0
jz CASE0
cmp AL, 1
jz CASE1
cmp AL, 2
jz CASE2
<default 程序段>
CASE0: <case0 程序段>
jmp CASEEND
CASE1: <case1 程序段>
jmp CASEEND
CASE2: <case2 程序段>
jmp CASEEND
CASEEND:
5个常用的INT 21H系统功能调用
一般步骤:
入口参数送到指定寄存器
系统功能号送到寄存器AH
用INT 21H执行功能调用
5…子程序结构
子程序的定义
子程序的定义是由过程定义伪指令PROC和ENDP实现,格式如下:
过程名 PROC [NEAR|FAR] ;子程序开始定义
… ;过程体
RET ;返回主程序
过程名 ENDP ;子程序定义结束
- 过程名(子程序名)为符合语法的标识符
- NEAR属性(段内近调用)的过程只能被相同代码段的其他程序调用。
- FAR属性(段间远调用)的过程可以被相同或不同代码段的程序调用。
- 主程序通常定义为FAR属性,这是因为主程序被看作DOS调用的一个子程序,以便执行完返回DOS。
子程序调用与和返回
子程序调用与返回由CALL和RET指令实现。
- 子程序和主程序在同一个代码段中称为段内调用;
- 子程序和主程序不在同一个代码段中,称为段间调用。
- 子程序返回指令负责把压入栈区的返回地址弹出送IP或CS∶IP,实现返回主程序继续往下执行。子程序的返回也分为段内返回和段间返回。
call调用
子程序的返回地址:CALL指令的下一条指令的地址
call near ptr subp (默认)
(1) 返回地址 (IP)入栈
(2) 转子程序 (IP) <- subp的偏移地址
call far ptr subp
(1) 返回地址 (CS、IP)入栈
(2) 转子程序 (CS) <- subp的段地址 (IP) <- subp的偏移地址
ret返回
ret [VAL] (VAL为一个正偶数)
- 段内返回(近返回)
IP <-(SP+1,SP)
SP <- SP+2
SP <- SP+VAL(如果选用了VAL) - 段间返回(远返回)
IP <-(SP+1,SP)
SP <- SP+2
CS <-(SP+1,SP)
SP <- SP+2
SP <- SP+VAL(如果选用了VAL)
VAL只是修改了返回后SP的值。
作用:调用子程序时先把所需参数入栈,调用结束后这些参数不再有用,可以修改堆栈指针使其指向参数入栈以前的值
寄存器内容的保护与恢复
保护现场和恢复现场的方法有两种:
- 利用堆栈(常用):利用入栈指令PUSH保护现场,利用出栈指令POP恢复现场。
- 利用内存单元:用传送指令MOV将寄存器的内容保存到指定的内存单元,需要时,再利用传送指令将指定的单元内容传送到指定的寄存器中加以恢复。
eg:
子程序的常见格式:
PROG PROC ;具有缺省属性的子程序
PUSH AX
PUSH BX
PUSH CX ;保护现场
PUSH DX
<函数体>
POP DX
POP CX
POP BX ;恢复现场
POP AX
RET ;返回断点处
PROC ENDP
子程序参数传递
子程序注释基本要素:
- 子程序名:SQROOT
- 子程序功能:求一个整数字数据的平方根的整数部分
- 入口条件:被开方数在CX中
- 出口条件:平方根在CX中
- 受影响的寄存器:CX及标志寄存器。
主程序与子程序之间的参数传递:
- 入口参数(输入参数):主程序提供给子程序
- 出口参数(输出参数):子程序返回给主程序
参数的形式:
- 数据本身(传值)
- 数据的地址(传址)
模块内参数传递常用的方法有以下四种:
- 寄存器法
- 用变量传递参数
- 堆栈法
- 参数地址指针法
寄存器法
把入口和出口参数存于约定的寄存器中。
子程序对带有出口参数的寄存器不能保护和恢复(主程序视具体情况进行保护)。
子程序对带有入口参数的寄存器可以保护,也可以不保护(一般建议保护)。
用变量传递参数
主程序和子程序直接采用同一个变量名共享同一个变量,实现参数的传递。
子程序和调用程序在同一个源文件
堆栈法
主程序将子程序的入口参数压入堆栈,子程序从堆栈中取出参数;
子程序将出口参数压入堆栈,主程序弹出堆栈取得它们
由于参数和子程序混杂在一起,存取参数时候必须小心计算它在堆栈中的位置,要注意堆栈区的状态,以及数据的保存和恢复
约定参数地址指针法
在主程序中建立一个地址表(连续内存空间),把需要传送给子程序的参数都存放在地址表中,然后把地址表的首地址通过寄存器传送到子程序中。
程序的连接
采用模块化程序设计,各模块之间会存在着相互调用,即一个模块会引用在另一个模块中定义的标识符(包括变量、标号、过程名等)。
标识符有两种:
- 在本模块中定义,供本模块使用的标识符称为局部标识符;
- 在一个模块中定义,而又在另一个模块中引用的标识符称为外部标识符。
需要遵循的原则:
- 声明共用的变量、过程等
- 实现正确的段组合
- 处理好参数传递问题
INCLUDE 包含伪指令
使用INCLUDE伪指令,把几个已独立编制好的.ASM文件在汇编时连接在一起,形成一个完整的.OBJ文件。
INCLUDE 文件名
- 利用INCLUDE伪指令包含其它文件,其实质仍然是一个源程序,只是分成几个文件书写;
- 被包含的文件不能独立汇编,是依附主程序而存在的;
- 合并的源程序之间的各种标识符,应该统一规定,不能发生冲突;
- 由于是源程序的结合,每次汇编都要包括对被包含文件文本的汇编,增加了汇编的时间。
在LINK连接时把各个模块连接在一起
把多个.OBJ文件连接成一个完整的.EXE文件。
- 各源程序要设置必要的段地址,至少要设置代码段
- 模块中要使用其它模块的标号时,用EXTRN语句说明;
- 本模块中存在可被其它模快引用的标号时,用PUBLIC语句声明。EXTRN和PUBLIC语句放在所有段的前面。
声明共用的变量、标号、过程:
PUBLIC 标识符 [,标识符...] ;定义标识符的模块使用
EXTRN 标识符:类型 [,标识符:类型...] ;调用标识符的模块使用
- 标识符是变量名、标号、过程名等。
- 类型是byte / word / dword(变量)或near / far(过程)。
- 在一个源程序中,public/extrn语句可以有多条。
- 各模块间的public/extrn伪指令要互相配对,并且指明的类型互相一致。
6…高级汇编语言技术
宏汇编
宏定义:
宏定义格式:
宏指令名 MACRO [形式参数表]
... ; 宏体
... ; 宏体
ENDM ;和子程序不同,ENDM之前不用写宏名
- 宏指令名由编程者自定,但必须符合标号的命名规则。
- MACRO和ENDM是一对伪指令,分别表示宏定义的开始和结束。
- 宏体必须是指令、伪指令及宏指令构成的程序段。
- 宏可以没有形参
- 实参和形参的个数可以不等,若调用时的实参个数多于形参个数,则多余的部分被忽略;若实参个数小于形参个数,则多余的形参假定为空(NULL)。
宏调用
宏调用格式:
宏指令名 [实参表]
- 宏指令名所指定的宏指令的定义必须放在该宏调用之前。
- 实参表通常与宏定义中形参表相对应。当需要使用多个实参时,各实参之间要用逗号分隔。
- 实参可以为空,也可以是常数、寄存器、存储单元、地址表达式、指令的操作码或者是操作码的一部分。
宏展开
在对源程序进行汇编时,自动用宏定义的内容(宏体)代替宏指令,叫宏展开。
宏展开的具体过程:当汇编程序扫描源程序遇到已有定义的宏调用(宏指令)时,即用相应的宏定义体取代源程序的宏指令,同时用位置匹配的实参对形参进行取代。这样,在程序的目标代码中,每个宏指令语句位置上都包含有相应宏体的目标代码,因此宏指令的使用不会减少程序的目标代码长度。
与宏有关的操作符
连接操作符(&)
在宏定义中,可以用连接操作符&作为形参的前缀。在宏展开时,&符前后的两个符号连接在一起构成一个新的符号。这个符号可以是操作码、操作数、或者是一个字符串
;宏定义:
Leap MACRO COND,LAB
J&COND LAB
ENDM
;宏调用:
LEAP Z, THERE
…
LEAP NZ, HERE
…
;宏展开:
JZ THERE
…
JNZ HERE
…
表达式操作符(%)
在宏调用时,表达式操作符%强迫后面的表达式立即求值,并把表达式的结果作为实参替换,而不是表达式本身。
DISP MACRO X
String DB ‘ANSWER:’, ‘&X’,‘$’ ;形参出现在字符串中,前面加“&”,替代后形成新的符号或字符串
ENDM
;宏调用:
DISP %(2*11-8)
;产生的宏扩展为:
String DB ‘ANSWER:’, ‘14’, ‘$’
;宏调用:
DISP 2*11-8
产生的宏扩展为:
String DB ‘ANSWER:’, ‘2*11-8’, ‘$’
转义操作符(!)
转义操作符指示汇编程序,把后面的字符当成普通的字符对待,而不使用它的特殊含义。宏调用的实参中若包含一些特殊字符(如宏操作符),就可以使用转义操作符。
例如,“!&”表示“&”不作为连接操作符使用,只作为符号“&”使用;“!%”表示“%”不作为表达式操作符使用,只作为百分号使用。
宏定义体中使用标号
宏指令一经定义便可在源程序中调用,若宏体中使用了标号或变量,在多次宏调用时就会出现多个相同标号或出现变量的重复定义,使用LOCAL伪指令可以解决这一问题。
LOCAL伪指令的一般格式:
LOCAL 标号及变量表 ;各标号、变量之间均用逗号分隔。
- LOCAL伪操作只用在宏定义体内,且LOCAL伪指令必须紧接MACRO伪指令之后,它们中间不能有注释和分号标志。
- 在处理各个宏调用时,汇编程序将自动以??0000,??0001,……,??FFFF替代LOCAL从伪指令列出的各个标号或变量,从而避免多次宏调用时出现多个相同标号或出现变量重复定义的问题。
宏嵌套
宏嵌套象子程序一样包括两种情况:
- 宏体中包括宏定义。此时必须首先调用最外层宏定义,然后才能调用内层宏定义。
- 宏定义的宏体中包括宏调用,即在宏体中调用宏体外定义的宏指令。在这种情况下要注意,其调用的宏指令必须先行定义;
重复汇编
有时候汇编语言程序需要连续的重复完成相同的或几乎完全相同的一组代码,这时候可以使用重复汇编。
REPT
格式:
REPT 整数表达式
重复体
ENDM
功能:使汇编程序对重复体作重复汇编,以整数表达式的值作为重复次数。
eg:
;把字符‘A’到‘Z’的ASCII码填入数组TABLE
CHAR = ‘A’
TABLE LABEL BYTE
REPT 26
DB CHAR
CHAR = CHAR+1
ENDM
;汇编后
DB 41H
DB 42H
DB 43H
……
DB 5AH
IRP伪操作
格式:
IRP 形参,<实参表>
重复体
ENDM
功能:使汇编程序对重复体作重复汇编,每作一次汇编就
依次将实参表中的一个实参取代重复体中的形参。
IRPC
格式:
IRPC 形参,字符串
重复体
ENDM
功能:使汇编程序对重复体作重复汇编,每作一次汇编就依次用字符串中的一个字符取代重复体中的形参。 与IRP相似,但实参必须是字符串。
库的使用
编程中将经常使用的,带有通用性的宏定义集中放在一个单独的磁盘文件——宏指令库(宏库)中,既可以减少程序的输入量,又方便程序修改。
建立宏库
为了在宏指令库中存放一个或多个宏指令定义(宏定义),可以用EDIT或其它文本编辑器建立宏库。
宏库的扩展名是*.mac。
调用插入伪指令INCLUDE
INCLUDE伪指令用来告诉MASM程序,将语句INCLUDE指出的文件完整地、全部地插入到它所在的位置。该文件是由汇编语言编写的源程序文件,包括宏库文件
删除宏库中部分宏伪指令PURGE
INCLUDE语句将文件的所有宏指令定义全部读到内存,但是有的宏定义对当前程序无用,却占用空间。为了删除汇编时引入到内存的无用宏定义,可在INCLUDE之后直接使用PURGE语句实现,删除操作的目的是使该宏定义内容为空,程序汇编时不再展开它。
例如,如果不用宏库中的“INOUTM”宏和“LRCF”,则可以使用下列形式就可以将它们删除。
INCLUDE MACROIO.mac
PURGE INOUTM , LRCF
宏指令名可以与指令助记符及伪指令名同名。在此情况下,宏指令的优先级较高,同名的指令或伪指令的原有功能失效。在利用这一方法改变了某个指令助记符或伪指令名的原有功能后,可以通过宏调用来使用新定义的功能。若要恢复其原有功能,可以使用清除宏定义的伪指令
例如:CBW是一个已定义宏名
CBW ;宏调用
……
PURGE CBW ;清除对CBW的宏定义
CBW ;将(AL)的符号扩展到AH
7…输入输出程序设计
I/O设备的数据传送方式
CPU与外设之间的接口信号:
- 数据信息:CPU和外设之间真正要交换的信息。
- 状态信息:用来反映外设接口电路或外设的状态,CPU可根据这些状态信息决定对外设的操作或控制。
- 控制信息:用于控制输入输出设备的启动或停止,设备的工作方式等。
以上三种不同性质的信息通过不同的端口传送,每个端口都有自己的地址,CPU寻址的是端口地址,而不是笼统的外设
主机与外设之间的数据传送(控制)方式:
- 直接存储器存取(DMA)方式
- 程序直接控制I/O方式(查询方式)
- 中断传送方式
直接存储器存取方式(DMA成组数据传送方式)
DMA(Direct Memory Access)方式能摆脱CPU的直接干预,利用硬件控制设备:DMA控制器(DMAC),实现外部设备与内存间的直接数据传送,以提高数据传输速率。
主要用于一些高速I/O设备,如磁盘,模数转换器(A/D)等。
在DMA模式下,CPU只须向DMA控制器下达指令,让DMA控制器来处理数据的传送,数据传送完毕再把信息反馈给CPU,这样就很大程度上减轻了CPU资源占有率,可以大大节省系统资源。
DMA控制器一般包括四个寄存器:控制寄存器(设置控制字:输入/输出、启动DMA等)、状态寄存器(DMAC状态)、地址寄存器(要传送数据块的首地址)、和字节计数器(要传送的数据字节数),这些寄存器在数据传送之前应该初始化。
在实现DMA传输时,由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。
系统完成DMA传送的步骤:
- 总线请求:DMA控制器向CPU申请使用总线
- 总线控制转移:CPU同意DMA控制器管理总线
- 数据传输:外设接口和存储器之间传输数据
传输数据块的首地址(在地址寄存器中)通过地址总线发出
传输的数据字节通过数据总线进行传送
地址寄存器加1,以指向下一个要传送的字节
字节字数器减1,如字节计数器非0,则继续传送 - 结束处理:DMA控制器放弃对总线的控制权
程序直接控制I/O方式
外设都是通过接口连接到计算机系统上,每个接口由一组寄存器组成,这些寄存器都分配有一个称为I/O端口的地址编码。CPU就是通过不同的端口号来选择各种外部设备的。
在80X86微机中,I/O端口编址在一个独立的地址空间中,这个空间允许设置64K个8位端口,或32K个16位端口。
所有I/O端口与CPU之间的通信都由IN和OUT指令来完成。其中:
- IN指令完成从I/O端口到CPU的数据传送(输入)
- OUT指令完成从CPU到I/O端口的数据传送(输出)
寻址方式。
- 直接寻址:只用于寻址00H~0FFH前256个端口,操作数表示端口号。
- 间接寻址:可用于寻址全部64K个端口,DX寄存器的值就是端口号,对大于0FFH的端口只能采用间接寻址方式。
输入指令IN
长格式:
IN AL, PORT (字节)
IN AX, PORT (字)
执行操作:
(AL) <- (PORT)(字节)
(AX) <- (PORT+1,PORT)(字)
短格式:
IN AL, DX (字节)
IN AX, DX (字)
执行操作:
(AL) <- ((DX))(字节)
(AX) <- ((DX)+1,(DX))(字)
ps:
只限使用AX或AL
不影响标志位
前256个端口号00H~FFH可直接在指令中指定(长格式)
如果端口号>=256,端口号–>DX(短格式)
输出指令OUT
长格式:
OUT PORT, AL (字节)
OUT PORT, AX (字)
执行操作:
(PORT) <- (AL)(字节)
(PORT+1,PORT) <- (AX)(字)
短格式:
OUT DX, AL (字节)
OUT DX, AX (字)
执行操作:
((DX)) <- (AL)(字节)
((DX)+1,(DX)) <- (AX)(字)
中断
中断:
当CPU正在执行某程序时,由于外界事件的需要向CPU发出申请,CPU暂停现行程序的执行而转去处理临时发生的事件,处理完后再返回到被中断程序的断点处,继续向下执行,这个过程称为中断。
中断是CPU和外部设备进行I/O的有效方法。它可以避免因反复查询外部设备的状态而浪费时间,提高CPU的效率。
中断服务程序:
在中断过程中执行的事件处理程序称为中断服务程序或者中断处理程序。
中断请求:
I/O设备或者事件需要CPU中断处理时,必须向CPU发出中断请求信号。当CPU收到该信号时,可引起中断。
中断源:
引起中断的原因,或者说发出中断请求信号的源称为中断源。通常,中断源有以下几种:
- 一般的输入输出设备。如键盘、打印机、通信接口等
- 数据通道中断源,如磁盘
- 实时时钟
- 故障源
- 为调试程序而设置的中断源
中断类型
8086可以管理256个中断,每个中断有相应的中断类型号。
软件中断(内中断):由程序安排的中断指令产生的中断、或CPU的某些错误结果产生的中断
- 由中断指令INT引起
- 由CPU的某些错误而引起
- 为调试程序(DEBUG)设置的中断。
硬件中断(外中断):由外设控制器或协处理器引起的中断(如键盘、鼠标等)。
- 可屏蔽中断(INTR):可被中断允许标志IF屏蔽。
- 非屏蔽中断(NMI):为电源错、内存或I/O总线的奇偶校验等异常事件保留的中断。它不受中断允许标志IF的屏蔽。整个系统只能有一个NMI(中断类型号为2)。
内中断
- 中断0——除法错中断;除数为0或商超过了寄存器的范围
- 中断1——单步中断;TF=1,允许单步调试
- 中断3——断点中断;用于断点调试(INT 3)
- 中断4——溢出中断(INTO);执行溢出中断指令,OF=1时产生
- 用户定义的软件中断(指令中断) :执行中断调用指令INT n产生的n号中断,如INT 21h
所有的内部中断都具有下述特点:
- 中断向量码或者包含在指令中,或者是预定的;
- 除单步中断外,内部中断都无法禁止;
- 除单步中断外,任何内部中断的优先级都比任何外部中断的高。
INT中断调用指令
指令汇编格式:
INT n
操作:
SP <- SP-2,(SP+1,SP) <- Flags
IF <- 0,TF <- 0
SP <- SP-2,(SP+1,SP) <- CS
SP <- SP-2,(SP+1,SP) <- IP
IP <-(n*4+1,n*4)
CS <-(n*4+3,n*4+2)
受影响的标志位:IF,TF
ps:
- n称为中断类型号,必须是0~255之间的立即数。
- 中断发生时的Flags内容也要保存起来
- 自动清除IF和TF,cpu转入中断处理程序后,不允许再响应新的中断和单步调试,如果此时还想允许外部中断,可以通过STI再把IF置为1.
IRET中断返回指令
指令汇编格式:
IRET
操作:
IP <- (SP+1,SP),SP <- SP+2
CS <- (SP+1,SP),SP <- SP+2
Flags <- (SP+1,SP),SP <- SP+2
受影响的标志位:所有状态标志位。
ps:
- IRET指令是任何中断服务程序的最后一条要执行的指令,它使CPU从中断服务程序返回被中断程序的断点处继续执行。
外中断
可屏蔽中断
- 其主要任务是接收外部设备的中断请求,然后根据优先级的高低和预先规定的排优规则决定哪个设备能够申请中断,由8259A向CPU发中断请求信号。如果CPU响应此中断请求,就自动转入相应的中断处理程序。
- 每个8259A有8个中断请求输入端,因此单个8259A可以处理8级中断,通过级连8259A最多可以管理64级中断。
注意:
从外设发出的中断请求到CPU响应中断,有两个控制条件起决定作用:
-
外设的中断请求是否被屏蔽:
中断屏蔽寄存器的I/O地址是21H,它的8位对应控制8个外部设备,每一位0表示允许中断,1表示禁止中断。 -
CPU是否允许响应中断:
CLI IF=0 关中断
STI IF=1 开中断
这两个条件分别由8259A的中断屏蔽寄存器(IMR)和标志寄存器中的中断允许位IF控制,2个条件需同时满足,才允许中断。
中断命令寄存器
在一次中断处理结束之前,还应给8259A的中断命令寄存器发出中断结束命令(End of Interrupt, EOI, 第5位)。中断命令寄存器的I/O端口地址为20H。
EOI=0以后将屏蔽掉对同级中断或者低级中断的响应。EOI=1允许对同级中断或者低级中断的响应。
在中断处理完成后,必须把EOI为置1。
L2~L0指定IR0-IR7中具有最低优先级的中断请求,第6、7位控制IR0-IR7中断优先级的顺序。
CPU对外部中断的响应步骤
当外设通过8259A向CPU提出申请,且CPU的IF=1时,CPU就挂起正在处理的任务,进行中断响应和处理。整个过程如下:
1.响应中断;
2.将标志寄存器的内容压栈;
3.清除中断允许标志位IF和陷阱标志位TF;
4.将代码段寄存器CS和指令指针IP的内容压栈;
5.根据中断码找到服务程序入口,且调用服务程序;
6.执行用户中断服务程序;
7.将保存在栈中的IP和CS的内容从栈中弹回到IP和CS;
8.将保存在栈中的标志寄存器的内容从栈中弹回到标志寄存器;
9.从中断返回。
以上过程的1 ~ 6由硬件自动完成,7 ~ 9执行IRET指令实现
中断优先级
由高到低:
除法错、INT n、INTO
NMI
INTR
单步中断
可屏蔽中断(INTR)的优先级又分为八级,正常情况下优先级由高到低依次是:
IR0,IR1,IR2,IR3,IR4,IR5,IR6,IR7
中断向量表
中断向量表:中断服务程序的入口地址
自定义中断
对新增加的中断功能要在中断向量表中建立相应的中断向量。
注意保存原中断向量。按保存-设置-恢复的顺序操作。
可调用DOS功能调用(21H)来存取中断向量:
-
设置中断向量:把由AL指定的中断类型的中断向量DS:DX放入中断向量表。
预置:AH=25H,AL=中断类型号,DS:DX=中断向量
执行:INT 21H -
读取中断向量:把由AL指定的中断类型的中断向量从中断向量表中取到ES:BX。
预置:AH=35H,AL=中断类型号
执行:INT 21H
返回:ES:BX=中断向量
eg:为已有的某个中断类型N设置中断向量
……
MOV AL, N
MOV AH, 35H
INT 21H
PUSH ES
PUSH BX ;保存原来的中断向量
PUSH DS
MOV DX, OFFSET INTHAND ;偏移地址=>DX
MOV AX, SEG INTHAND
MOV DS, AX ;段地址=>DS
MOV AL, N
MOV AH, 25H
INT 21H ;设置新的中断向量
POP DS
……
POP DX
POP DS
MOV AL, N
MOV AH, 25H
INT 21H ;恢复原来的中断向量
RET
……
INTHAND: ;中断处理程序
……
IRET
中断处理程序的结构
与子程序相似,可用定义过程的方式来定义中断处理程序。所有编写过程的一些规定和要求均适用于中断处理程序,包括用伪指令PROC/ENDP定义过程,类型为远类型。
中断程序的调用和编写步骤:
主程序:
(1)设置中断向量
(2)设置 CPU 的中断允许位 IF
(3)设置设备的中断屏蔽位
中断处理子程序:
(1)保存寄存器内容
(2)如允许中断嵌套,则开中断 (STI)
(3)中断处理功能
(4)关中断
(5)送中断结束命令(EOI)给中断命令寄存器
(6)恢复寄存器内容
(7)IRET中断返回
eg:中断处理程序的一般结构的程序
INTPRG PROC FAR
STI ;若允许中断嵌套
PUSH DS
PUSH DX
PUSH AX
PUSH BX
…… ;中断处理
CLI ;关中断
MOV AL, 20H ;发中断结束命令EOI
OUT 20H, AL
POP BX ;恢复现场
POP AX
POP DX
POP DS
IRET ;中断返回
INTPRG ENDP
;由于IRET将恢复中断前的标志,故IF也被恢复。
8…BIOS及DOS功能调用
BIOS与DOS简介
BIOS
IBM PC系列机在只读存储器ROM中固化有一组外部设备驱动与管理软件,组成PC机基本输入输出系统(Basic Input/Output System,BIOS),它处于系统软件的最低层,又称ROM BIOS。
BIOS主要包括以下一些功能:
- 系统自检及初始化:系统加电启动时对硬件进行检测、对外部设备进行初始化、设置中断向量、引导操作系统等。
- 系统服务:为操作系统和应用程序提供系统服务,这些服务主要与I/O设备有关,如读取键盘输入、显示等。
- 硬件中断处理:提供硬件中断服务程序。
计算机系统软件就是利用这些基本的设备驱动程序与管理软件,完成各种功能操作。
DOS
磁盘操作系统(Disk Operating System),他是早期PC机的重要操作系统之一,主要完成对文件、设备、内存的管理。
主要包括三个模块:
- IBMBIO .com:DOS在ROM BIOS的基础上开发的一组输入输出设备处理程序,是DOS与ROM BIOS的接口
- IBMDOS .com:在IBMBIO.COM的基础上,DOS还开发有文件管理程序和一些处理程序
- COMMAND .com:DOS的命令处理程序。
BIOS和DOS
BIOS和基本DOS的作用
用户可通过使用BIOS和DOS系统提供的这些功能模块子程序(中断子程序调用),来编制直接管理和控制计算机硬件设备的底层软件(主要是完成I/O操作)
用户编程原则
- 尽可能使用DOS的系统功能调用。一般来说,使用DOS操作比使用相应功能的BIOS操作更简易,而且DOS对硬件的依赖性更少些。
- 在DOS功能不能实现情况下,考虑用BIOS功能调用。
- 在DOS和BIOS的中断子程序不能解决问题时,使用IN/OUT指令直接控制硬件。
调用BIOS/DOS功能子程序
调用BIOS/DOS功能子程序的基本方法:
- BIOS/DOS的每个功能都对应着一个中断服务程序
- 采用int中断的BIOS/DOS中断属于软件中断
- 其中:
DOS中断:n= 20H~3FH
BIOS中断:n=5~1FH
自由中断:n=40H~FFH(用户可自定义)
DOS中断和BIOS中断使用方法 :
- 将调用参数装入指定的寄存器
- 如需功能调用号,把它装入AH
- 如需子功能调用号,把它装入AL
- 按中断号调用DOS或BIOS
- 检查返回参数是否正确
DOS调用与BIOS调用两者的异同:
- DOS功能调用在更高层次上提供了与BIOS类同的功能。
- 调用BIOS中断程序比调用DOS的复杂一些,但运行速度快,功能更强;
- DOS功能调用只适用于DOS环境,而BIOS功能调用不受任何操作系统的约束;
- 某些功能只有BIOS具有。
键盘I/O
字符码与扫描码
键盘主要由3种基本类型的键组成:
- 字符数字键(传送一个ASCII码字符):A~Z, 0~9, 以及常用符号%, $, #等。
- 扩展功能键(产生一个动作):Home、PgUp以及功能键F1~F10等。
- 组合使用的控制键(改变其他键所产生的字符码):如Alt,Ctrl,Shift等。
PC机系列的键盘触点电路按16行×8列的矩阵来排列,用单片机Intel8048来控制对键盘的扫描。
按键的识别采用行列扫描法,即根据对行线和列线的扫描结果来确定闭合键的位置,这个位置值称为按键的扫描码,通过数据线将8位扫描码送往主机。
当在键盘上“按下”或“放开”一个键时,如果键盘中断是允许的(8259A 21H端口的第一位等于0),就会产生一个类型9的中断,并转入到BIOS的键盘中断处理程序。
该处理程序从8255可编程外围接口芯片的输入端口读取一个字节,这个字节的低7位是按键的扫描码。最高位为0或者为1,分别表示键是“按下”状态还是“放开”状态。按下时,取得的字节称为通码,放开时取得的字节称为断码。
eg:
按下ESC键取得的通码为01H(00000001B)
放开ESC键取得的断码为81H(10000001B)
BIOS键盘处理程序将取得的扫描码转换成相应的字符码,大部分的字符码是一个标准的ASCII码;没有相应ASCII码的键,如Alt和功能键(F1~F10),字符码为0
键盘输入
转换成的字符码以及扫描码存储在BIOS数据区的键盘缓冲区KB_BUFFER中:
Buffer_Head DW ? ;键盘缓冲区头指针
Buffer_ Tail DW ? ;键盘缓冲区尾指针
KB_Buffer DW 16 DUP(?) ;键盘缓冲区的缺省长度为16个字
KB_Buffer_End Label Word
键盘缓冲区是一个先进先出的循环队列。虽然缓冲区的本身长度为16个字,但出于判断“队列满”的考虑,它最多只能保存15个键盘信息。
当缓冲区满时,系统将不再接受按键信息,而会发出“嘟”的声音,以示要暂缓按键。当Buffer_Head=Buffer_Tail时,说明缓冲区为空,表示无键盘输入。
BIOS的INT 09H和INT 16H中断处理程序是一对相互配合的程序,其中09H(硬件中断)向键盘缓冲区写入,16H(软件中断)从键盘缓冲区读出,09H是有键按下的时候产生硬件中断并触发;16H是应用程序调用的时候起作用。
键盘状态字
在计算机键盘上除了可输入各种字符之外,还有一些控制键(如:Ctrl、Alt、Shift等)、双态键(如:Num Lock、Caps Lock等)和特殊请求键(如:Print Screen、Scroll Lock等)。
键盘中的控制键和双态键是非打印按键,它们是起控制或转换作用的。当按下控制键或双态键时,系统要记住其所按下的按键。为此,在计算机系统中,特意安排的一个字节来标志这些按键的状态,我们称该字为键盘状态字节(KB_Flag)
键盘状态字节的各位含义如下图所示。
INT 16H的2号功能,可以把键盘状态字节(KB_Flag)回送到AL,其中1表示按下。
BIOS键盘中断(INT 16H)
中断处理程序包括3个不同的功能:
- AH=0:从键盘读字符到AX寄存器中。其中AL=字符码,AH=扫描码。如果键盘缓冲区为空,等待键盘输入
- AH=1:读键盘缓冲区字符到AX寄存器中,并置ZF标志位。不等待键盘输入
若ZF=0,则AL=字符码,AH=扫描码
若ZF=1,则表示缓冲区空 - AH=2:读取键盘状态字节。(AL=键盘状态字节)
DOS键盘功能调用(INT 21H)
AH | 功能 | 调用参数 | 返回参数 |
---|---|---|---|
0 | 程序终止(同INT 20H) | CS=程序段前缀 | |
1 | 键盘输入并回显 | AL=输入字符 | |
2 | 显示输出 | DL=输出字符 | |
6 | 直接控制台I/O | DL=FF(输入) DL=字符(输出) | AL=字符 |
7 | 键盘输入(无回显) | AL=输入字符 | |
8 | 键盘输入(无回显) 检测Ctrl-Break | AL=输入字符 | |
9 | 显示字符串 | DS:DX=串地址 '$'结束字符串 | |
A | 键盘输入到缓冲区 | DS:DX=缓冲区首地址 | (DS:DX+1)=实际输入的字符数 (DS:DX)=缓冲区最大字符数 |
B | 检验键盘状态 | AL=00(有输入) AL=FF(无输入) | |
C | 清除输入缓冲区并 | AL=输入功能号 |
如果程序要求能接收功能键或数字组合键必须进行两次DOS功能调用,第一次回送00,第二次回送扫描码
eg:要求用户通过输入F1、F2、F3来选择,其他按键则产生错误信息。
…
mov ah,7
int 21h
cmp al,0
je get_char
jmp error
get_char:
mov ah,7
int 21h
cmp al, 3bh ;F1?
je option1
cmp al, 3ch ;F2?
je option2
cmp al, 3dh ;F3?
je option3
jmp error
…
error:
…
输入字符串:
- INT 21H的0A号功能,DS:DX=缓冲区首地址。
- 缓冲区的第一个字节保存最大字符数(逻辑上限是255),由用户设定,若输入的字符数大于此数,PC机会发出“嘟嘟”声,光标不再移动。
- 第二个字节是实际输入字符的个数。这个数据由功能10自动填入。
- 两个字节之后就是用户输入的字符串,以回车键结束(也会占用一个字节)。
- 因此缓冲区的大小应为:最大字符数(包括回车)+2
清除键盘缓冲区:
- Int 21的功能0ch能清除键盘缓冲区,然后执行在AL中指定的功能。
- AL中指定的功能可以是1,6,7,8或0AH。
检验键盘状态:
- INT 21H的0B号功能可以检验一个键是否被按动
- 如果按下一个键,则AL=0FFH,否则AL=0
- 无论哪种情况都将继续执行下一条语句
显示器I/O
显示器两种显示方式:
- 文本方式:屏幕分成若干行和列,在每个网格位置上显示字符。
- 图形方式:将屏幕分成m*n的点阵,在每个点的位置显示像素
文本显示方式:字符属性
在常用的文本显示模式下,屏幕被划分成25行,每行可显示80个字符,所以,每屏最多可显示2000(80×25)个字符
为了便于标识屏幕上的每个显示位置,我们就用其所在行和列来表示之,并规定:屏幕的左上角坐标为(0, 0),右下角坐标为(24, 79)。
对应屏幕上的每个字符位置,主存空间都有相应的存储单元与之对应,因此可说是显示屏幕是“存储器的映像”。
对应显示屏幕上的每个字符,在存储器中由连续的两个字节表示,一个字节表示ASCII码,另一个字节保存字符的属性。
单色字符显示
对单色显示,字符的属性确定了该字符的显示方式,如字符是否闪烁、是否高亮度、是否反向显示等
彩色字符显示
显示彩色字符时,属性字节可以选择显示字符的前景颜色和背景颜色。
前景颜色有16种可以选择,背景颜色有8种可以选择。
闪烁和亮度只应用于前景。(BL为闪烁位,I为亮度位)
显示存储器
在25 X 80的文本显示方式下,屏幕可有2000个字符位置,因每个字符需要用两个字节,所以每屏显存容量需要4K。若显存有16K,则可以保存4屏的显示字符数据。
对VGA的80列显示方式,0页的起始地址是B800:0000,1页的起始地址是B800:1000,2页的起始地址为B800:2000。
屏幕上某一字符在显存中的偏移地址可由下列公式算出:
Char_Offset = Page_Offset + (row * width + column) * byte
byte是表示一个字符所用的字节数,这里:byte=2。
BIOS显示中断调用INT10(例子看ppt)
10H中断调用为显示器中断,共有17种功能。下面列出几种主要功能的使用情况。
设置光标类型(1号功能)
入口参数:AH=1(功能号),CH=光标开始行,CL=光标结束行。只用CH、CL的低4位,若CH的第4位为1,光标不显示。
出口参数:无。根据CX给出光标的大小。
设置光标位置(2号功能)
入口参数:AH=2(功能号),BH=页号,DH=行号,DL=列号。
出口参数:无。根据DX确定了光标位置。
读当前光标位置(3号功能)
入口参数;AH=3(功能号),BH=页号。
出口参数:DH=行号,DL=列号,CX=光标大小。
初始窗口或向上滚动(6号功能)
入口参数:AH=6,AL=上滚行数,CX=上滚窗口左上角的行、列号。DX=上滚窗口右下角的行、列号。BH=空白行的属性。
出口参数:无。当滚动后,底部为空白输入行。 如果AL=0,清除屏幕。
初始窗口或向下滚动(7号功能)
入口参数:AH=7,AL=下滚行数,CX=下滚窗口左上角的行、列号。DX=下滚窗口右下角的行、列号。BH=空白行的属性。
出口参数:无。当滚动后,顶部为空白输入行。如果AL=0,清除屏幕。
读当前光标位置的字符与属性(8号功能)
入口参数:AH=08H,BH=页号。
出口参数:AL为读出的字符,AH为字符属性。
在当前光标位置写字符和属性(9号功能)
入口参数:AH=9,BH=页号,AL=字符的ASCII码,BL=字符属性,CX=写入字符重复次数。
出口参数:无。
DOS显示功能调用INT21H
显示字符 (02H功能)
入口参数:DL=字符;光标跟随移动
出口参数:显示一个字符(检CTRL_BREAK)
显示字符 (06H功能)
入口参数:DL=字符;光标跟随移动
出口参数:显示一个字符(不检CTRL_BREAK)
显示字符串 (09H功能)
入口参数:定义要显示的字符串,字符串尾应为‘$’,作为结束显示的标志。DS : DX = 字符串的首地址
出口参数:无,显示字符串,遇‘$’停止显示,光标随动。
串行通信口I/O
计算机与外设交换信息的过程中:
- 并行通信:多位数据通过多条数据线同时传送。
- 串行通信:多位数据通过同一条数据线按位传送。
并行通信就是把一个字符的各数位用几条线同时进行传输。与串行通信(一位一位传输)相比,在相同传输率下,并行通信的信息实际传输速度快、信息率高。
但并行通信比串行通信所用电缆多,随着距离的增加,电缆的开销会成为突出的问题。所以,并行通信总是用在数据传输率要求较高,而传输距离较短的场合。
按通信进行的过程,分为:单工、半双工、全双工通信方式:
- 单工:只容许数据由一方发、一方收,单向通讯。
- 半双工:容许双向通讯,但是收发只能分时共用一路通道。
- 全双工:容许数据同时双向收发。
串行通信可以分为两种类型:同步通信、异步通信
异步通信:
- 一个字符一个字符地传输,每个字符一位一位地传输,传输一个字符时,以起始位开始,然后传输字符本身的各位,接着传输校验位,最后以停止位结束该字符的传输。
- 一次传输的起始位、字符各位、校验位、停止位构成一组完整的信息,称为帧(Frame)。
- 帧与帧之间可有任意个空闲位。
- 起始位之后是数据的最低位。
- 起始位:在数据发送线上规定无数据时电平为1,当要发送数据时,首先发送一个低电平0,表示数据传送的开始,这就是起始位。
- 数据位:真正要传送的数据,由于字符编码方式不同,可以是5位、6位、7位、8位、9位等多位,数据位是由低位开始,高位结束(低位在前、高位在后)
- 奇偶校验:数据发送完后,发送奇偶校验位,以检验数据传送的正确性,这种方法简单,容易实现。
- 停止位:表示数据传送的结束,可以是1位、1.5位或2位。高电平1有效。
同步通信
- 同步通信方式不给每个字符都加起始位和停止位,而把字符顺序的连接起来,组成一个数据块(首尾相连的数据串),把这样一个数据块称为一个信息桢
- 在数据块的开始加上一个同步字符,而在信息的末尾加有一定的差错检验字符,其格式如下
波特率和传输率
- 串行通信中,传输速率是用波特率来表示
- 波特率是指单位时间内传送二进制数据的位数(简写为bps)
- 在计算机里,每秒传输多少位和波特率的含义是完全一致的
- 收、发双方的波特率必须一致
IBM PC通信端口
- IBM PC和80X86兼容机可以连接4个通信端口,他们的编号为BIOS为:0-3,DOS为:1-4。
- 程序每次只能对其中一个端口进行存取。
DOS串行通信口功能调用
- 使用DOS命令可以设置串行通信参数,如波特率,校验位,字长和终止位。
- 常用的波特率:2400、4800、9600、19200、38400等。
- 格式:MODE COMm: b ,p ,d , s
- m: 1~4,
- b:波特率,用波特率的最高两位来表示
- p:校验位(N:无校验,O:奇校验,E:偶校验)- - d:数据的字长(5,6,7,8,默认值是7)
- s:终止位位数(1,1.5,或2)。
DOS串行通信口功能调用
文件存取I/O
文件操作既可以通过BIOS的中断服务INT 13H,也可以使用DOS系统功能调用INT 21H
INT 13H提供的文件操作要求给出磁头号、磁道号、扇区号等磁盘物理参数,比较复杂。而INT 21H提供的文件操作只要求给出文件名,相对要简单的多
文件代号式磁盘存取:
- 用户在处理一个文件时,必须给出完整的路径名,一旦文件的路径名送入操作系统,系统就返回给用户一个16位的二进制控制字,称为文件代号
- 以后对该文件进行读写操作时,就用这个文件代号去查找相应的文件。
- 相对文件全名,用文件代号指称文件效率更高,避免重复处理字符串
路径名和ASCIZ串
ASCIZ串最后一个字节为0,其余字节是指示文件位置的ASCII码字符串。
[d:][path]filename.exe, 00
其中d为驱动器名,path为路径名,.exe为文件名后缀。
用变量定义的形式就写作:
filename1 DB ‘C:\SAMPLE.TXT’,00
filename2 DB ‘c:\test\run.exe’, 00
DOS已经预定义了文件代号0到4与标准输入输出设备对应,即
- 0 ── 标准输入设备,键盘;
- 1 ── 标准输出设备,屏幕;
- 2 ── 错误输出的标准设备,屏幕;
- 3 ── 标准辅助设备(通信端口);
- 4 ── 标准打印设备。
这5个文件代号长期处于打开状态,应用程序可以直接使用。
对建立或打开的文件,其代号从5开始顺序排列,在任一时刻最多只能同时打开5个文件。当程序执行时,调用的每一个文件都必须分配一个唯一的文件代号。
在汇编语言或者操作系统看来,文件与标准输入输出设备都是数据流,两者的差别在于操作系统支持对文件的随机存取,而标准输入输出设备只能顺序存取。向标准输出设备写一段数据意味着把这些数据送到屏幕显示,从标准输入设备读一段数据则是从键盘读入一串数据。
错误返回码
当用针对文件的DOS系统调用时,返回CF=0代表操作成功,返回CF=1代表操作失败。如果操作失败,会通过AX返回错误代码。
文件属性
每个文件都有自己的属性,用一个字节表
属性字节存放到CX寄存器中。
文件指针
使用DOS系统功能调用INT 21H 建立文件或者打开文件成功后,DOS系统自动提供一个文件指针来指示文件的当前位置。
文件指针是一个32位二进制数,建立文件或者打开文件成功后,文件指针的初值为0,也就是指向文件的开始位置。
以后每次对文件的读写操作,系统自动修改文件指针的值,使文件指针指向下一次要读写的位置,每次文件指针的移动位移量就等于读写文件的字节数。
常用扩展磁盘文件管理系统功能调用(21H)
eg:从文件file1中读取10个字符到file2文件中。
data segment
fname db 'c:\file1.dat',00
fname2 db 'c:\file2.dat',00
dta db 80h dup(0)
data ends
code segment
assume cs:code,ds:data
start:mov ax,data
mov ds,ax
mov es,ax
mov dx,offset fname ;Open source file
mov al,0 ;read file
mov ah,3dh ;Open the file
int 21h
mov si,ax ;file number
mov bx,si
mov dx,offset dta ;read from the source file
mov cx,10 ;read 10 bytes
mov ah,3fh ;read the file
int 21h
mov di,ax ;实际读入的字节数
mov ah,3eh ;close the source file
int 21h
mov dx,offset fname2 ;make the new file
mov cx,0
mov ah,3ch
int 21h
mov si,ax ;文件代码
mov dx,offset dta ;write into the file
mov cx,di ;write bytes
mov bx,si ;file number
mov ah,40h ;write
int 21h
mov bx,si ;close the file
mov ah,3eh
int 21h
mov ah,4ch
int 21h
code ends
end start