汇编语言程序设计(第二版)第3章.ppt

上传人:小飞机 文档编号:6586405 上传时间:2023-11-15 格式:PPT 页数:93 大小:318.50KB
返回 下载 相关 举报
汇编语言程序设计(第二版)第3章.ppt_第1页
第1页 / 共93页
汇编语言程序设计(第二版)第3章.ppt_第2页
第2页 / 共93页
汇编语言程序设计(第二版)第3章.ppt_第3页
第3页 / 共93页
汇编语言程序设计(第二版)第3章.ppt_第4页
第4页 / 共93页
汇编语言程序设计(第二版)第3章.ppt_第5页
第5页 / 共93页
点击查看更多>>
资源描述

《汇编语言程序设计(第二版)第3章.ppt》由会员分享,可在线阅读,更多相关《汇编语言程序设计(第二版)第3章.ppt(93页珍藏版)》请在三一办公上搜索。

1、第3章 基本指令与简单程序设计,3.1 寻址方式 3.2 基本指令 3.3 单个字符的输入输出 3.4 源程序的基本格式 3.5 顺序程序设计 习题三,3.1 寻 址 方 式,3.1.1 立即数型寻址方式 这种寻址方式又称为立即数或立即寻址,是直接把参与操作的数据写在指令中。用汇编语言书写时,操作数可以是各种数制下的数值,也可以是带单引号的字符。比如,下面的MOV指令中源操作数都是立即寻址的简单情况。汇编语言要求指令在一行写完,指令后面所带的分号表示该行的后续内容是注释。,MOV AL,30;源操作数是十进制形式的立即数30MOV AX,3030H;源操作数是十六进制形式的立即数3030HMO

2、V AL,11001101B;源操作数是二进制形式的立即数11001101BMOV AL,3;源操作数是字符形式的立即数3,即其ASCII值33H尽管MASM支持把立即数加上方括号的写法,比如上面的第一条指令也可以写成MOV AL,30但是,这会导致初学者概念上的混淆,建议读者不要使用这种写法。,立即寻址还有一些比较复杂的情况。如果操作数是由数值和运算符构成的表达式,汇编程序在翻译时会计算出表达式的值,并把计算结果以立即数形式翻译到机器指令中。比如下面的指令中源操作数就是这种情况:MOV AL,3-30H该指令中的源操作数是一个可直接计算的表达式。汇编程序会计算出表达式的值是3,并以3作为立即

3、寻址方式的源操作数。,3.1.2 寄存器型寻址方式 这种寻址方式比较简单,就是在指令的操作数部分写寄存器的名称。可用的名称有:AH,AL,BH,BL,CH,CL,DH,DL 8位通用寄存器;AX,BX,CX,DX,SI,DI,BP,SP 16位通用寄存器;CS,DS,ES,SS 16位的段寄存器。,下面的指令中所有操作数都是寄存器寻址方式。MOV AL,BLMOV AL,DHMOV BP,SPMOV AX,SIMOV AX,CSMOV DS,DX,【注意】指令指针IP、标志寄存器PSW以及所有标志位的代号CF、ZF、OF等都不能作为寄存器寻址方式的操作数,不允许出现在汇编语言的任何指令中。另外

4、,8088汇编语言还规定,不允许用MOV等具有赋值功能的指令修改CS的值,也就是说,类似于“MOV CS,AX”试图对CS赋值的指令在8088汇编语言中是不允许的。,3.1.3 内存型寻址方式 内存型寻址方式是指参与操作的数据在内存中,因此必须指明操作数究竟在内存的什么地方,即指出内存的逻辑地址。逻辑地址的段地址部分来自某个段寄存器。每一个内存型操作数都有一个不需要在指令中写出的缺省段寄存器与之对应,如果就以这个缺省段寄存器的值作为段地址,则指令中只要确定偏移地址即可。但是,有时指令中需要使用其它段寄存器而不用缺省段寄存器作为段地址,这时就要先写出需要使用的段寄存器的名字,后面加冒号“:”,再

5、接偏移地址的各种写法。这种不用缺省段寄存器而明确写出段寄存器名称的方式称为“段跨越”。按照确定操作数偏移地址的不同方法,内存型寻址又细分为5种具体情况,分别称为直接寻址、寄存器间接寻址、寄存器相对寻址、基址变址和相对基址变址。,3.1.3.1 直接寻址 这种寻址方式是在指令中直接写明操作数所在的偏移地址。在汇编语言中,这个偏移地址通常以变量的形式出现,在指令中就是直接写变量的名字。变量名字与偏移地址之间存在固定的对应关系,在源程序中写变量的名字,汇编程序会把名字翻译成相应的偏移地址。确立这种对应关系的方法是定义变量。定义变量的具体写法在第5章中加以说明,在此需要说明的是,定义变量时会说明它的类

6、型(字节、字或者双字),定义后的变量就有了一个确定的偏移地址,程序中还会有伪指令说明变量对应的缺省段寄存器是哪一个。也就是说,每个已定义的变量都有缺省段寄存器与之对应,都有固定的偏移地址和类型。,设buf是已经定义的字节型变量,指令“MOV AL,buf”中源操作数寻址方式就是直接寻址。用方括号把变量名字括起来是直接寻址的基本写法。上述指令中的“buf”表示以变量buf对应的偏移地址和缺省段寄存器中的值作为完整的逻辑地址,操作数在逻辑地址所确定的内存单元中。假定上述指令中变量buf的缺省段寄存器是DS,执行上述指令时DS的值是1234H,buf的偏移地址是0ABCDH,则物理地址的形成可用图3

7、.1表示,图中数据均为十六进制。,图3.1 直接寻址方式下操作数的物理地址的形成,直接寻址方式可以使用段跨越。下面是两个使用段跨越的例子:MOV AL,CS:bufMOV AL,ES:buf使用段跨越时物理地址的形成方式,只要在图3.1中把段寄存器DS换成段跨越符号所指明的段寄存器即可。MASM V5.0还支持不写方括号的直接寻址方式,前面的3个例子可以写成如下形式:MOV AL,bufMOV AL,CS:bufMOV AL,ES:buf,变量占据内存空间的大小是以字节为单位的,一个变量并不一定只占一个字节,2.3节就已说明了一个变量占多个字节时在内存中如何安排。汇编语言中还有类似高级语言的数

8、组变量的形式。假定变量buf是存放5个数据的数组,每个数据是字节型,所以共占5字节。现在需要取它的第3个数据送到寄存器AL中,又该怎么写呢?很简单,可以在变量名的后面用加号连接一个数值的形式书写,比如:MOV AL,buf+2,对字节型数组变量buf,分别用buf、buf+1、buf+2、buf+3和buf+4依次对应它所占据的5个字节的偏移地址。汇编程序在翻译这种“变量数值”的写法时,先找出变量名所对应的偏移地址,再与另一个数值相加(如果是用减号连接则计算出两者的差),计算结果作为操作数的偏移地址,以直接寻址方式翻译到机器指令中。如果buf变量所占据的5个字节中依次存放的是1、2、3、4、5

9、,上述指令取到AL中的数据会是3而不是2。尽管下面的写法与上述指令完全等同,但也不建议使用:MOV AL,buf2,3.1.3.2 寄存器间接寻址 这种寻址方式是把一个基地址寄存器或变址寄存器中的字型数据当作偏移地址,写法是以该寄存器的名字加上方括号。这表示寄存器的内容作为偏移地址,而不是参与操作的数据本身,操作数需要到内存中去找。注意与寄存器寻址相区分。下面的例子中源操作数都是寄存器间接寻址方式。MOV AL,BXMOV AL,SIMOV AL,BP,【注意】并不是所有的通用寄存器都能加上方括号,按寄存器间接寻址方式到内存去寻找操作数。8088汇编语言规定,只有基址寄存器BX和BP,变址寄存

10、器SI和DI共4个寄存器可以作为间接寻址方式使用的寄存器。,如果需要,寄存器间接寻址方式也可以使用段跨越,比如:MOV AL,DS:BPMOV AL,ES:BXMOV AL,SS:DI 直接寻址和寄存器间接寻址是汇编语言中内存型操作数最常用的两种寻址方式。如果与高级语言相比较,直接寻址相当于高级语言中的整数、字符等类型的简单变量,而寄存器间接寻址则相当于指向某种数据类型的指针变量。,3.1.3.3 寄存器相对寻址 寄存器相对寻址又称作变址寻址,是以一个基址寄存器或变址寄存器中的值与一个字型数据相加,相加的结果作为偏移地址。相加的结果如果超过16位二进制,超出部分会被忽略。从书写形式上说,以下几

11、个例子中的源操作数都采用了正确形式的寄存器相对寻址方式,其中的标识符buf是已经定义的变量。但是,建议读者在编程时尽量使用第1、第3和第5个例子的写法,以清楚地表明偏移地址是两个数据相加而来。其中的方括号不能省略,方括号中不允许出现一个变量减一个寄存器的写法。,MOV AL,buf+SI MOV AL,bufDI MOV AL,buf+BP MOV AL,bufBXMOV AL,BX+15 汇编语言中还支持更复杂的寄存器相对寻址写法,下面是几个这样的例子。MOV AL,buf+BX+3MOV AL,BX-30H,对于“buf+BX+3”的写法,汇编程序会先把buf对应的偏移地址与数值3相加,得

12、到的和与BX一起,以寄存器相对寻址的形式翻译到机器指令中;对于“BX-30H”,汇编程序会把-30H翻译成相应的补码形式,即0FFD0H,并把减法转变成加法。前面几个例子说明了寄存器相对寻址方式下偏移地址的处理方法,但段地址从何而来呢?这里当然也存在缺省段寄存器的问题。,寄存器相对寻址的缺省段寄存器按下列规则处理:(1)如果是“变量+寄存器”的形式,以变量对应的缺省段寄存器为准。(2)如果是“寄存器+数值”的形式,则以寄存器对应的缺省段寄存器为准。(3)寻址方式中不允许同时出现两个或两个以上的变量相加的情况,但可以出现两个变量相减。减法表示两个变量偏移地址的差值,这个差值不再作为变量看待,而是

13、当作数值,此时缺省段寄存器按第(2)条处理。以指令“MOV AL,BX+15”为例,源操作数采用了寄存器相对寻址,形成物理地址的方式如图3.2所示。,图3.2 寄存器间接寻址方式下物理地址的形成,【例3.1】设变量buf1和buf2定义在同一个段中,偏移地址分别是102H和3ACH,两个变量对应的缺省段寄存器都是ES,寄存器BX和BP的值分别是2000H和3000H。试计算下列各指令中源操作数的偏移地址值,并判断缺省段寄存器是哪一个。(1)MOV AL,buf1+BX(2)MOV AL,BX+13(3)MOV AL,BP+buf1-buf2(4)MOV AL,buf2+BP-4000H,【解】

14、(1)偏移地址为102H+2000H2102H段寄存器为buf1对应的缺省段寄存器ES。(2)偏移地址为2000H+13200DH段寄存器为BX对应的缺省段寄存器DS。(3)偏移地址为3000H+102H3ACH2D56H段寄存器为BP对应的缺省段寄存器SS。(4)偏移地址为3ACH+3000H4000H0F3ACH段寄存器为buf2对应的缺省段寄存器ES。,【例3.2】设arr是一个整型数组变量,其中存放了10个字型带符号整数,用C语言写出相应的变量定义,编写程序段显示该数组中各元素的值;并说明如果使用汇编语言的寄存器相对寻址方式arr+BX分别去取各元素,BX应如何变化。【解】C语言的变量

15、定义为 int arr10;程序段写作:for(i=0;i10;i+)printf(“%dn”,arri);,在汇编语言中,如果要用arr+BX的形式分别访问各元素,则BX应分别取值为十进制的0、2、4、6、8、10、12、14、16、18,即每次加2,因为每个整数(字型带符号数)占2字节。在C语言中,下标变量i从0到9逐个递增,而汇编语言中BX每次加2,请仔细观察两者的对应关系,体会使用上的差别。例3.2同时还说明“寄存器相对寻址”这个名称中相对二字的含义,是指寄存器中的值代表操作数的地址相对于变量起始地址的偏差值。,3.1.3.4 基址变址 这种寻址方式是用一个基址寄存器与一个变址寄存器的

16、值相加,计算结果作为操作数的偏移地址。如果加法运算的最高位向前有进位则被忽略。书写形式是用加号把两个寄存器连接起来,并加上方括号。汇编语言的语法规定,必须用一个基址寄存器与一个变址寄存器相加。基址寄存器只有BX和BP,变址寄存器只有SI和DI,组合起来只可能出现4种情况。下面的4个例子中的源操作数就是这4种组合的书写形式。,MOV AL,BX+SIMOV AL,BX+DIMOV AL,BP+SIMOV AL,BP+DI,这里再次出现段地址在哪个段寄存器的问题。上面的写法仍然采取了缺省段寄存器方式,缺省段寄存器由基址寄存器确定,对应关系是:当基址寄存器是BX时,缺省段寄存器是DS;当基址寄存器是

17、BP时,缺省段寄存器是SS。上面的例子中源操作数都是基址变址方式,前两个都使用DS作为逻辑地址中的段地址,而后两个则用SS。如果需要,基址变址寻址方式也可以使用段跨越。,汇编语言的语法还允许把基址变址方式中的两个寄存器分别写在两个方括号中,连在一起不用加号“+”。下面的写法与前面的4个例子在效果上完全对应相同。MOV AL,BXSIMOV AL,BXDIMOV AL,BPSIMOV AL,BPDI,3.1.3.5 相对基址变址 这种寻址方式是把一个基址寄存器和一个变址寄存器的值以及一个字型数据三者相加,结果作为操作数的偏移地址。字型数据可以是一个变量,也可以是一个数值。段地址部分可以用段跨越方

18、式明确指出段寄存器的名字。没有段跨越符号时,缺省段寄存器分两种情况:一是寻址方式中出现变量时,以变量对应的缺省段寄存器为准;另一种情况是寻址方式中没有变量而是直接写数值,这时以基址寄存器对应的缺省段寄存器为准。下面的几个例子中,假设buf是已定义的变量,对应的缺省段寄存器是DS,指令后面的注释对操作数逻辑地址中的段地址做了简单说明。,MOV AL,buf+BX+SI;段寄存器是buf对应的缺省段寄存器DSMOV AL,buf+BP+DI;段寄存器是buf对应的缺省段寄存器DSMOV AL,BX+SI+30;段寄存器是BX对应的缺省段寄存器DSMOV AL,BP+SI-30;段寄存器是BP对应的

19、缺省段寄存器SSMOV AL,ES:BP+SI-30;以段跨越方式明确指明段寄存器是ES,MOV AL,bufBXSIMOV AL,bufBP+DIMOV AL,BXSI+30MOV AL,-30BPSI,与基址变址类似,相对基址变址在书写时也可以用两个方括号把基址寄存器和变址寄存器括起来,并且不写加号“+”。下面是相对基址变址寻址方式在书写上的几种变形。,相对基址变址最典型的应用是对二维数组元素的访问,它具有类似双下标的书写形式,但与高级语言的双下标又有很大的区别。在C语言中,若定义了一个二维数组变量buf存放m行n列的矩阵,可以直接用行列坐标的形式bufij表示第i行第j列的元素(以左上角

20、为第0行第0列)。若要逐个取某一行元素,可以固定i的值不变,让j依次取值0、1、n-1;若要逐个取某一列元素,可以固定j的值不变,让i依次取值0、1、m-1。这种方式简洁明了,充分体现了高级语言的优势。在汇编语言中,bufBXSI的含义完全不同,它代表了buf+BX+SI,这里BX和SI的值不代表矩阵的行与列。如果想用bufBXSI表示出第i行第j列元素的偏移地址,则需要让BX和SI分别取如下值:BX i n kSI j k,其中n是矩阵的列数,k是每个矩阵元素占据的内存字节数,在编写程序时,n和k都是常量。若要逐个取某一行元素,可以固定BX的值不变,让SI先取初值0,然后每次加k;若要逐个取

21、某一列元素,可以固定SI的值不变,让BX先取初值0,然后每次加nk。实际上,汇编语言要求编程人员自己根据数据在内存中的存放情况考虑基址寄存器和变址寄存器的变化。,3.1.4 外设型寻址方式 给操作对象逐个编号是计算机的基本处理方法。在8088系统中把内存的每个字节进行编号,形成内存的物理地址,类似地也把控制各种外部设备的接口中的各部件编排号码,每个号码对应的一个外设部件称为一个外设端口,号码本身就是外设地址,又称外设端口号。外设是多种多样的,各自的接口也不同,但接口中的各个部件却有一个共同特点,就是能够以1字节为基本单位存放来自系统总线的数据,或者向系统总线提供数据。从这一特点上看,接口中的每

22、个基本部件与内存的一个字节在操作方式上并没有什么差别。于是有些计算机在设计上把内存与外设端口综合在一起,统一地编排一套地址,以地址本身来区分操作对象是内存还是外设,这种地址编排方式称为统一编址或混合编址。,8088采取的是另一种地址编排方式,把外设端口与内存分开来,各编各的地址,这种编址方法称为独立编址。前面已经介绍了8088内存地址的有效范围是1 MB,而它的外设地址有效范围是64 KB。那么,如果写出一个地址,比如300H,如何判断操作对象是内存还是外设呢?这个问题从地址本身是无法解决的,8088系统以指令来区分操作对象的种类。用于内存操作的指令很多,MOV指令就是其中一个,但用于外设操作

23、的指令就只有两条:IN和OUT。输入输出指令的具体用法在第8章中加以说明,这里只是用它们作例子解释外设寻址方式。,外设寻址方式比较简单。一种是把外设地址直接写在指令中,类似于对内存的直接寻址方式,但是不加方括号,比如:IN AL,61H;第2个操作数表示61H号外设端口OUT 43H,AL;第1个操作数表示43H号外设端口这种寻址方式要求外设地址不超过255。另一种方式是把外设地址放在寄存器DX中,类似于内存型的寄存器间接寻址方式,但是也不加方括号,比如:IN AL,DXOUT DX,AL,3.2 基 本 指 令,3.2.1 MOV指令【指令格式】MOV d1,d2【功能】取d2操作数的值,放

24、到d1操作数指定的位置。这是一种典型的双操作数指令,d1称为目的操作数,d2称为源操作数。这条指令一般用于对某个寄存器或内存变量进行赋值,与高级语言中的赋值语句大致相当,两者的一个重要区别在于,高级语言中的赋值语句有计算能力而MOV指令没有。,【注意事项】(1)目的操作数d1是数据送往的地点,不允许是立即寻址方式。(2)如果d1是使用通用寄存器的寄存器寻址方式,则源操作数可以是立即数、寄存器寻址或内存型寻址方式中的任何一种。(3)当d1是段寄存器时,d2只能是通用寄存器或内存型寻址方式,不能是立即数,也不能是另一个段寄存器。(4)不允许两个操作数都是内存型寻址方式。,(5)如果两个操作数都有确

25、定的类型,则两者的类型必须相同,即要么都是8位的字节型,要么都是16位的字型。(6)如果两个操作数中只有一个可以确定类型,则另一操作数的类型按可确定类型的操作数同型处理;当一个操作数是寄存器,另一操作数是变量,且两者类型不同时,变量可以临时改变类型,保证与寄存器类型一致。(7)如果d1是寄存器间接寻址或者基址变址寻址方式,d2是不超过255的立即数,这时从任何一个操作数都不能确定类型,需要在d1操作数的前面用伪指令BYTE PTR或者WORD PTR指明是字节型操作还是字型操作。,(8)指令中的内存型操作数可以使用段跨越。(9)MOV指令不影响标志寄存器的值。,下面的各个例子从汇编语言的语法上

26、讲都是正确的,其中buf是已定义的字节型变量。MOV AL,BL;字节型操作MOV AL,3;源操作数是字符形式的立即数,3即是33HMOV BX,BX;字型操作,d2操作数按d1定类型MOV DX,3;字型操作,d2操作数按d1定类型MOV CL,buf+BP;字节型操作MOV CX,buf+BP;字型操作,临时改变buf的类型,以buf+BP及其下一字节拼装成字型操作数MOV AX,WORD PTR buf;字型操作,临时改变buf的类型,以buf的前两个字节拼成一个字作为源操作数,MOV DS,AX;字型操作MOV BYTE PTR BX,3;用伪指令指明是字节型操作MOV WORD P

27、TR SI,3;用伪指令指明是字型操作MOV buf,3;字节型操作,变量buf被定义为字节型MOV DS,BXDI;字型操作,从内存中取一个字给段寄存器DS,下面的指令是不符合语法规则的,注释中给出了相应的说明。MOV AL,BX;违反规则(5),操作数类型不同MOV BX,buf;违反规则(4),两个操作数都是内存型MOV 3,AL;违反规则(1),目的操作数用了立即数MOV AX,DX;DX不能用作间接寻址的寄存器,见节MOV AX,BL;BL虽然是BX的一部分,也不能用作间接寻址的寄存器MOV DS,CS;违反规则(3)MOV CS,DX;不能用MOV指令修改CS的值MOV AX,IP

28、;指令指针IP不允许作为操作数MOV BX,WORD PTR AL;AL是字节型,不能用伪指令改变其类型,3.2.2 ADD指令【指令格式】ADD d1,d2【功能】把目的操作数d1与源操作数d2相加,结果放入目的操作数中替代原有的数据。该指令不改变源操作数d2的值。ADD指令是一条双操作数指令,用于把两个操作数相加。在汇编语言中,寄存器或内存中存放的数据相当于高级语言的变量,涉及到这类数据的计算不能像高级语言那样只写一个简明的表达式,也没有任何内部函数可供使用,而必须用各种运算指令一步步写出计算的过程,这是汇编语言程序篇幅很长的一个原因。,【注意事项】作为双操作数指令,ADD指令在语法规定上

29、大部分与MOV指令是一样的,但也有它特别的地方:(1)同MOV指令中的规则(1)、(2)、(4)(8)。(2)两个操作数都不允许是段寄存器。(3)ADD指令本身并不区分相加的两个数是无符号数还是带符号数,因为相加的结果在二进制形式下是一样的。(4)该指令根据运算结果设置标志寄存器中的各条件标志位,设置方法见第4章。,【例3.3】设AX3AF7H,试确定指令ADD AL,AH执行后AX的值。【解】根据ADD指令的功能,把AL中的0F7H与AH中的3AH相加:0F7H+3AH31H,有进位指令执行后,AX3A31H。ADD指令把两个字节型(或字型)数据相加,如果最高位向外有进位,在计算加法的结果时

30、会被忽略,但进位情况会在标志位上反应出来。另一方面,AH作为源操作数,其值没有改变,所以指令执行后AX的值是3A31H。,例3.3中还有几个问题需要明确。首先是加法运算的两个操作数分别来自寄存器 AX的高8位部分AH和低8位部分AL,由于指令中要求把相加的结果送回目的操作数AL,所以AX的低8位部分被修改成加法运算的结果31H。尽管运算中有向外的进位,但这个进位只通过标志寄存器反映出来,并不加到AX的高8位AH上,作为源操作数,AH的值并不会改变。其次,从AX的原值以及加法指令本身都无法确定操作数是无符号数还是带符号数。如果当作无符号数,则相当于24758,结果应该是305,但实际结果是49。

31、这是因为305已经超出了字节型无符号数的表示范围,这正是运算结果向外有进位的含义。如果把两个加数当作带符号数,则相当于(-9)(+58)(+49)。不论操作数带符号与否,在8位二进制数的范围之内,两者运算结果是一样的。,3.2.3 SUB指令【指令格式】SUB d1,d2【功能】用目的操作数d1作为被减数,源操作数d2作为减数,把两者相减的差送回目的操作数d1中。该指令不改变源操作数d2的值。减法指令在语法上的注意事项与ADD指令完全相同,请参照前面的说明。另外有一点需要强调,如果出现两数相减而不够减时,会自动向前借位,借位情况将反映到CF标志位上。如果运算本身将超出范围,则结果会有所偏差。这

32、并不是计算的错误,而是受运算位数的限制。第4章还将说明如何从标志位上判断运算结果是否超出有效范围。,【例3.4】设AX3AF7H,试确定指令SUB AH,AL执行后AX的值。【解】根据SUB指令的功能,有3AH0F7H43H运算中有借位,结果AX的值是43F7H。如果把操作数当作无符号数,则相当于58-247,而结果是67。这个结果显然与正确值是有偏差的,产生这一现象的原因是,作为无符号数58减247本就不够减,这一现象会在标志位上有所反映。如果把操作数当作带符号数,则相当于(+58)-(-9)(+67)。,3.2.4 MUL指令【指令格式】MUL d【功能】完成无符号数的乘法运算。根据操作数

33、d的类型分两种情况:如果d是字节型,则把AL的值与d相乘,16位的积放到AX中;如果d是字型,则把AX的值与d相乘,32位的积放到DX和AX中,DX放积的高16位,AX放低16位。乘法运算必须有两个乘数,MUL指令规定其中一个乘数一定放在AL或AX中,积的存放位置也是由指令本身确定。这种由指令本身限定必须使用的操作数称为隐含操作数。乘法的另一乘数需要在指令中用操作数的形式指明。,【注意事项】(1)MUL指令只能完成无符号数乘法运算,如果是带符号数则需要用另外的指令。(2)两个乘数的位数必须相同,如位数不同则需要转换,或做适当处理,运算结果的位数是乘数位数的两倍。(3)指令中的操作数d不允许是立

34、即数,只能用寄存器或内存型寻址方式,但不能是段寄存器,内存型操作数可以使用段跨越。(4)操作数d必须有确定的类型,当使用内存型寻址方式且不能确定类型时,必须用BYTE PTR或WORD PTR伪指令加以说明。,【例3.5】设变量buf是字节型,试判断下列乘法指令在语法上是否正确。(1)MUL AL(2)MUL AX(3)MUL buf(4)MUL CS:buf+BX(5)MUL WORD PTR buf(6)MUL BYTE PTR BP+DI(7)MUL DS(8)MUL 1024H(9)MUL BX(10)MUL AX,BX,【解】根据有关语法规则可做出如下判断。(1)正确。把ALAL的1

35、6位结果送到AX中。(2)正确。把AXAX的32位结果送到DX(高位)和AX(低位)中。(3)正确。乘数是字节型的buf,使用缺省段寄存器,计算ALbuf。(4)正确。乘数是字节型,采用段跨越方式。(5)正确。临时改变变量buf的类型,以buf+1作为高8位,buf作为低8位,构成16位的乘数,使用buf对应的缺省段寄存器。(6)正确。用伪指令指明乘数是字节型,段地址取自基址寄存器BP对应的缺省段寄存器SS。,(7)错误。违背规则(3),试图使用段寄存器DS作乘数。(8)错误。违背规则(3),乘数试图使用立即寻址方式。(9)错误。违背规则(4),乘数BX无法确定是字节型还是字型。(10)错误。

36、不符合指令格式,MUL指令只能带单操作数。,【例3.6】设x和y是字型变量,a和b是字节型变量,试编写指令序列,完成下列表达式的计算,并把结果送到变量y中。,(a-30)(b+1)+x,【解】MOV AL,a;取出变量a的值送到AL中SUB AL,30;计算a-30,结果在AL中MOV BL,b;取出变量b的值送到BL中ADD BL,1;计算b+1,结果在BL中MUL BL;计算ALBL,结果在AX中,16位ADD AX,x;用前面计算得到的16位的积加上变量x的值MOV y,AX;结果送到变量y中,3.2.5 DIV指令【指令格式】DIV d【功能】完成无符号数的除法运算。根据操作数d的类型

37、分两种情况:如果d是字节型,则用AX的值作为被除数,指令中的操作数d作为除数,除法运算结果包括商和余数两部分,字节型的商放到AL中,字节型的余数放到AH中;如果d是字型,则用DX的值作为高16位,AX的值作为低16位,组成32位的被除数,操作数d作为除数,计算结果,字型的商放到AX中,字型的余数放到DX中。与乘法指令类似,DIV指令也限定了隐含的操作数,包括被除数和结果的存放位置。特别的是,除法运算的结果包括商和余数两部分。,【注意事项】(1)DIV指令只能完成无符号数除法运算,带符号数的除法需要另外的指令。(2)被除数必须是字型或双字型,如果是其它类型则需要采取适当的方式进行处理,商和除数的

38、类型相同,位数是被除数位数的一半。(3)同MUL指令注意事项(3)、(4)。在除法运算中,除数、商和余数的位数相等,而被除数的位数是它们的两倍,商就有可能超过限定的位数。下面的例3.7的最后一个小题就有这样的问题。,【例3.7】设AX6C8FH,DX30C7H,BX0E21FH,说明下列除法指令的执行结果。(1)DIV BH(2)DIV BX(3)DIV BL【解】(1)指令完成AXBH的操作,运算结果,商在AL中,是7AH,余数在AH中,是0DBH。(2)指令完成(DX,AX)BX的操作,运算结果,商3739H放在AX中,余数6AA8H在DX中。(3)6C8FH1FH的商是380H,余数是0

39、FH,应该分别放在AL和AH中,但显然商放不下,所以该指令将导致操作异常。,3.3 单个字符的输入输出,3.3.1 DOS的1号子功能单字符输入【功能】从键盘上读取一个按键的ASCII码值。【入口参数】AH中放子功能号1。【出口参数】AL中是按键的ASCII码。,【说明】(1)该功能只要求在执行INT 21H指令时AH中的值是1,而不论AH是在何时、以何种指令被赋的值。这一点对所有DOS系统功能调用都是一样的。(2)调用时,计算机的屏幕上将出现一个闪烁的光标,等待操作人员按键。当有键被按下后,该按键的ASCII值将被放入AL。(3)与C语言的标准输入函数scanf不同,1号子功能在输入时只需要

40、按一个键即可,而不像scanf那样一直要等到操作人员按下回车键。,(4)每次只读取一个按键,并且按下的符号会显示在屏幕上,退格键、回车键等特殊按键也会被当作有效输入。下面是几个特殊按键与AL中读取结果的对应关系:按ESC键 AL=1BH按回车键 AL=0DH按退格键 AL=08H(5)如果读回按键的ASCII码值是0,表示按下的是一个扩展ASCII范围的键,比如F1键,这时可再读一次,以获得该键的扩展ASCII值。(6)该功能调用不改变除AL外的其它寄存器的值,包括AH中的1。,【例3.8】编写一个程序段,从键盘读入一个数字键,计算出对应的数值,放入寄存器DL中。不考虑按键不是数字键的情况。【

41、解】程序段如下:MOV AH,1INT 21H;调用DOS的1号子功能SUB AL,0;把读入键的ASCII值减去数符0的ASCII值MOV DL,AL;结果放到DL中 键盘上的数字键09对应的ASCII值是30H39H,因此在例3.8中,读入的按键必须经过转换才能得到正确的数值,在程序段中表现为把读到AL中的键减去字符0(即ASCII值30H)。,3.3.2 DOS的2号子功能单字符输出【功能】在屏幕上光标当前所在位置显示一个字符,并把光标向后移一格。【入口参数】AH中放子功能号2,DL中放待输出字符的ASCII值。【出口参数】无。,【说明】(1)该子功能在执行时不论DL中数据的来源如何,都

42、当做是一个ASCII值,经过内部转换变成相应字符的形状显示在屏幕上。(2)该子功能调用会改变寄存器AL的值,所以必要时可把AL的值放在另一寄存器或内存中临时保存,其它寄存器的值都不受影响。(3)有些特殊的ASCII值可以控制计算机产生特定的效果。比如,当DL中放7并调用该子功能时,计算机的扬声器会发出“嘀”的一声响,而屏幕上并没有任何字符输出。部分特殊效果与ASCII值的对应关系见表3.1。,表3.1 特殊输出效果相应的ASCII值,【例3.9】编写程序段完成回车换行功能。【解】程序段如下:MOVAH,2MOV DL,13;回车符的ASCII码值INT 21HMOV DL,10;换行符的ASC

43、II码值INT 21H;输出回车符时已把AH放2,且调用后未变,3.4 源程序的基本格式,3.4.1 行的格式“行”是程序编写的基本单位。源程序的一行可以是一条指令、伪指令,也可以是变量定义。一个指令行的基本格式如下:标号:指令助记符 操作数,操作数;注释各个部分的顺序不可改变,其中的方括号表示可选项。指令助记符是一个指令行必不可少的成分。指令本身还规定了助记符的后面应带有一个操作数、两个操作数,还是根本没有操作数。指令助记符与操作数之间至少要有一个空格,其余的空格仅仅是为了使程序清晰,便于阅读。最简单的情况下,一行就只有一个指令助记符。,标号用来表示一行指令所在的逻辑地址,后面的冒号不可省略

44、。标号是一个标识符,通常是一个由字母开头的符号串。关于标识符完整的命名规则参见第5章变量的命名规定。标号的作用是用于确定跳转的目的地,这将在第4章中举例说明。写注释是为了增加程序的易读性,是写给编程人员自己或其他阅读者看的。阅读汇编语言程序是一件相当困难的事,甚至一个人看自己以前编写的程序都需要花费大量的时间和精力,所以养成写注释的习惯不仅仅是方便别人,也会方便自己。汇编程序在把源程序翻译成目标代码时会忽略一行中分号“;”及其后面的内容,不做任何处理。就易读性而言,把程序中的所有指令助记符对齐,把注释对齐,把变量名、标号等自定义标识符对齐,都是良好的编程习惯。,汇编语言源程序中允许空行。在较长

45、的源程序中,把完成一个功能的各行连在一起不空行,而用一到两个空行把完成不同功能的程序段分开也有助于程序的阅读。如果需要,可以在一行上只写注释,甚至连续写多行注释,这当然要求行的第一个有效符号(不计前面的空格)是分号。有些人习惯于把标号单独占一行,下一行再写指令。这也是允许的,这时标号所代表的地址仍然是其后面的第一条有效指令的地址。如果在这样的两行之间再插入若干个空行就不是好习惯了。,3.4.2 段的格式 由若干行加上段的起止标记构成源程序的一个“段”,与第2章所说的逻辑段相对应。段的基本格式是:段名SEGMENT 段名ENDS,一个段中可以写若干行指令,也可以写若干变量定义。其中只写指令的段称

46、为代码段或指令段,只写变量定义的段称为数据段。把指令序列和变量定义分别写在代码段和数据段中是较好的编程习惯,有利于阅读程序。但有时为了程序设计的需要,也可以把指令和变量定义混合着写在一个段中,这种做法属于汇编语言编程的特殊方式,很容易造成程序的逻辑错误,建议不要使用。,3.4.3 程序格式 一个汇编语言源程序由至少一个段和表示程序结束的伪指令END构成。源程序的基本格式如下:,段名1SEGMENT段名1ENDS段名2SEGMENT段名2ENDS段名nSEGMENT段名nENDSEND标号,3.4.4 完整程序实例【例3.10】编写一个程序,在屏幕上显示一个大写字母A。【解】,codeSEGME

47、NT;段开始标记ASSUME CS:code;CS是code段中所有标识符的缺省段寄存器main:MOV AH,2;标号main代表本行的MOV指令的偏移地址MOV DL,A;把字母A的ASCII值放到DL中INT 21H;调用DOS系统2号子功能,显示一个字符MOV AH,4CH;准备4CH号子功能号码INT 21H;以4CH号子功能把机器控制权交还给DOScodeENDS;段结束标记END main;程序结束标记,程序从标号main处开始执行,程序中只有一个段,段的名字是code。第1行和第8行是code段的开始和结束伪指令。第3行到第7行是指令序列,其中第3行上有一个标号main,与第9

48、行程序结束伪指令END后面的main相对应,说明程序的第1条指令在什么地方。输出字母A的操作是由程序的第3行到第5行完成的,紧接其后的第6、7行调用DOS的4CH号子功能,在程序完成应做的工作后,把计算机的控制权交还给DOS操作系统。第2行是一条伪指令,用来说明code段中的所有变量、标号(如果有的话)的缺省段寄存器是CS,所以END后面的标号main仅仅是偏移地址,段地址则是它所在的code段的段地址。,3.5 顺序程序设计,一个程序通常都是从外部读取一些数据,然后进行处理,再把处理的结果送到输出设备上。对于一些简单的数据处理,只要把完成处理的各个步骤用汇编语言描述清楚,让计算机按指令编写的

49、次序从头到尾一条条执行即可,这样的程序结构称为顺序结构。,【例3.11】用十进制输出一个按键的ASCII值。【分析】问题并不复杂,首先需要从键盘接收一个按键,键的ASCII值一定在0到255之间,最大是十进制的3位数。作为整数是不能直接输出的,所以先把这个值除以100,得到的商是百位数字,再把刚才的余数除以10,商是十位数字,余数是个位数字,把这3个数字分别加上30H转换成相应的ASCII码,依次输出到屏幕上即可。,【解】codeSEGMENTASSUME CS:codemain:MOV AH,1INT 21H;读一个按键MOV AH,0;准备做AX100,所以要把AH清0MOV BL,100

50、;除数不允许用立即数,把除数放在BL中DIV BL;除以100MOV CL,AL;保存商,即百位数字 ADD CL,30H;把百位数字转换成ASCIIMOV AL,AH;取除以100的余数到AL中,作下一次的被除数MOV AH,0;被除数高位部分清0MOV BL,10;准备除数10DIV BL;除以10,商在AL中,余数在AH中,ADD AL,30H;商是十位数字,转换成ASCIIADD AH,30H;余数是个位数字,转换成ASCIIMOV BX,AX;用BX保存转换后的十位和个位数字MOV AH,2MOV DL,13INT 21H;输出回车符MOV DL,10INT 21H;输出换行符MOV

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号