第3章-语言程序设计_第1页
第3章-语言程序设计_第2页
第3章-语言程序设计_第3页
第3章-语言程序设计_第4页
第3章-语言程序设计_第5页
已阅读5页,还剩174页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第4章

汇编语言程序设计4.1分支程序结构4.2循环程序结构 4.3子程序结构4.4Windows应用程序编程4.5与C++语言的混合编程2教学重点三种结构程序设计①顺序结构②分支结构③循环结构

知识点4.1汇编语言的格式4.2语句行的构成4.3指示性语句4.4指令语句4.5汇编语言程序设计及举例汇编是把汇编语言程序翻译成机器语言描述的目标程序的过程。汇编程序是完成汇编任务的程序。用汇编语言编写的程序。2.汇编程序连接程序的主要功能是把多个目标文件及库文件连接在一起形成可执行文件。3.连接程序

1.汇编语言源程序汇编语言源程序汇编目标程序连接可执行程序从汇编语言源程序到可执行程序的生成过程如图所示。4.1汇编语言源程序格式下面是一个示范性的完整的汇编语言源程序。;lt301b.asm(文件名)stack segmentstack ;定义堆栈段

dw512dup(?) ;堆栈段有512字(1024字节)空间stack ends ;堆栈段结束data segment ;定义数据段string db’Hello,Everybody!’,0dh,0ah,’$’data endscode segment’code’ ;定义代码段

assumecs:code,ds:data,ss:stackstart: movax,data ;建立DS段地址

movds,ax movdx,offsetstring movah,9 int21h movax,4c00h int21h ;利用功能调用返回DOScode ends ;代码段结束

endstart;汇编结束汇编语言源程序格式完整的汇编语言源程序由若干个段组成源程序中段的数目可以根据实际需要而定,但其中必不可少的是代码段每个段由若干个语句行构成汇编语言源程序格式NAME1SEGMENT

语句语句NAME1ENDSNAME2SEGMENT

语句

语句NAME2ENDSEND<标号>.........汇编语言源程序语句行的格式汇编语言源程序中的每个语句行可以由四项组成,格式如下:若操作数项有多个操作数,则操作数之间使用逗号分隔各部分之间至少用一个空格作为间隔[名字]操作码[操作数][;注释]带方括号的项是可选项名字项名字项是指一个标号、变量、段名或过程名等。操作码项操作码项是一个操作码的助记符,它可以是指令、伪指令或宏指令名。操作数项操作数项提供为执行所要求的操作而需要的信息。操作数项可以是常数、寄存器、标号、变量或由表达式组成。注释项注释项用来说明程序或语句的功能。";"为识别注释项的开始。";"也可以从一行的第一个字符开始,此时整行都是注释,常用来说明下面一段程序的功能。

在调试程序过程中可适当使用“;”4.2语句行的构成4.2.1标记1、IBM宏汇编的字符集(1)字母大写字母小写字母(2)数字0、1……9(3)特殊字符图4-12、界符图4-2语句中有了界符,可以不用分隔符。3、常量常量是指汇编时已经有确定数值的量它又分成多种形式:(1)十进制常量(2)十六进制常量(3)二进制常量(4)八进制常量(5)字符串常量用单引号或双引号括起来的单个字符或多个字符,其数值是每个字符对应的ASCII码的值例如:'d''AB''Hello,Everybody!'字符串常量标识符(Identifier)一般最多由31个字母、数字及规定的特殊符号(如_、$、?、@)组成,不能以数字开头。一个程序中,每个标识符的定义是唯一的,还不能是汇编语言采用的保留字4、标识符由程序员自由建立的、有特定意义的字符序列。5、保留字保留字(ReservedWord)是汇编程序已经利用的标识符,主要有:指令语句助记符——例如:MOV、ADD伪指令语句助记符——例如:DB、EQU操作符——例如:OFFSET、PTR寄存器名——例如:AX、CS第3章IBM宏汇编中的保留字见P86表4-16、注释以“;”打头。4.2.2符号1、寄存器一种标识符,要符合标识符的组成规则。2、变量存放在存储单元中的操作数是变量。在程序中出现的是存储单元地址的符号。变量名变量名是数据存储单元偏移地址的符号表示命名规则:

遵守标识符的命名规则变量的属性变量具有以下三种属性:①段值名字对应存储单元的段地址②偏移值名字对应存储单元的偏移地址③类型变量名的类型可以是BYTE(字节)、WORD(字)和DWORD(双字)等变量的定义变量一般在除代码段以外的其他段定义。变量需定义才能被引用变量用数据定义语句DB、DD等定义3、标号标号是指令存储单元偏移地址的符号表示命名规则:

遵守标识符的命名规则标号的属性标号具有以下三种属性:①段值标号对应存储单元的段地址②偏移值标号对应存储单元的偏移地址③类型标号类型可以是NEAR(近)和FAR(远),分别表示段内或段间标号的定义和引用标号在代码段定义和引用LOOP1:MOVAL,[SI]...JMPLOOP1标号定义标号引用变量和标号的区别不同:标号对应的存储单元中存放的是指令,后面跟冒号;变量对应的存储单元中存放的是数据,后面不跟冒号。相同:都是存储单元的符号地址。4、数利用一个标识符表达的一个数值常数若使用有意义的符号名来表示,可以提高程序的可读性,同时更具有通用性符号常量实例符号定义

DosWriteChar=2 CarriageReturn=13符号应用(右边程序段等价左侧的符号形式)

movah,DosWriteChar;movah,2 movdl,CarriageReturn;movdl,135、其他符号指示性语句(伪指令)的操作码、宏指令等

汇编语言大小写不敏感4.2.3表达式后面介绍4.2.4语句汇编语言源程序中的语句有两种:指令语句和指示性语句(伪指令)。指令语句——使CPU产生动作、并在程序执行时才处理的语句,就是第3章学习的指令伪指令(Directive)——不产生CPU动作、在程序执行前由汇编程序处理的说明性语句,例如,数据说明、变量定义等等两种语句的格式标号:

指令语句助记符操作数,操作数;注释名字伪指令助记符参数,参数,…

;注释指令语句格式:伪指令格式:两种语句在格式上的主要区别4.3指示性语句(伪指令语句)符号定义语句数据定义语句段定义语句过程定义语句结束语句4.3.1符号定义语句EQU=PURGE1、等值语句EQU语句格式:符号名EQU表达式功能:给符号名定义一个值,或定义为别的符号名,甚至可以定义为一条可执行的指令。如:ABCEQU220XYZEQUABCCOUNTEQUCXCBDEQUADDEQU语句在未解除之前,不能重新定义。如:ABCEQU220ABCEQU330错误!2、解除语句PURGE功能:用来解除已用EQU语句定义的符号,解除后的符号可以重新定义。如:PURGEABCABCEQU330PURGE的格式为:PURGE符号1,符号2,...,符号n3、等号语句=语句格式:符号名=表达式功能:与EQU语句类似,但是能对符号进行重新定义。如:EMP=6EMP=7EMP=EMP+14.3.2数据定义语句格式:[变量名]伪操作符操作数1,操作数2,...[;注释]功能:定义一个变量的类型,给存储器赋初值,或者仅仅给变量分配存储单元,而不赋予特定的值。变量名可以没有。这种情况,汇编程序将直接为操作数分配空间,无符号地址。设置变量名是为了方便存取它指示的存储单元汇编程序将把所定义的数据按先后次序连续分配存储空间,所起的名字也只代表第一个字节单元的偏移地址。常用伪操作符DB(DefineByte)定义字节DW(DefineWord)定义字DD(DefineDoubleWord)定义双字DQ(DefineQuadword)定义四个字DT(DefineTenbyte)定义十个字节操作数可以是常数、表达式、字符串或重复操作符DUP,不能是寄存器名.例1DATASEGMENTDATA_BYTEDB10,4,10H,?DATA_WORDDW100,100H,-5DATA_DWDD3*20,0FFFDHDATA_ASCDB‘AB’DATA_WORD1DW‘AB’DATAENDS说明:(1)操作数?,不放入任何数,仅保留存储空间.(2)操作数在内存中所占的字节数与伪操作符所定义的字节数一致.(3)操作数在汇编后以补码形式存放.(4)只有用DB定义时,才允许字符串的长度超过2个字节。DATA_BYTE0AH04H10H---64HDATA_WORD00H00H01HFBHFFHDATA_DW3CH00H00H00HFDH00HFFH00HDATA_ASC41H42H42H0000H0001H0002H0013H000AH0003H0004H0005H0006H0007H0008H0009H000BH000CH000DH000EH000FH0010H0011H0012H0014H41H0015HDATA_WORD1例2DATASEGMENTADWMBUFDWBUF-A

EMPEQU220

MDB2DUP(1),2DUP(2,2DUP(1,2))DDDBUFDATAENDS说明:(1)操作数为变量时,则存放其地址。若用DW定义,则存放其偏移地址;若用DD定义,则存放其偏移地址和段地址,第一个字为偏移地址,第二个字为段地址.(2)重复操作符DUP可以嵌套.重复操作符DUP的格式:nDUP(初值[,初值…])括号中为重复的内容,n为重复的次数。A04H00H02H00H01HBUF01H02H01H02H01HM02H02H00HDATA低8位0000H0001H0002H0013H000AH0003H0004H0005HDDATA高8位0006H0007H0008H0009H000BH000CH000DH000EH000FH0010H0011H0012H01H02H01H02H02H用DW或DD伪操作可以把变量或标号的偏移地址(DW)或偏移地址和段地址(DD)存入存储器。用DD伪操作存入地址时,第一个字为偏移地址,第二个字为段地址。定位伪指令ORG语句格式:ORG常数表达式功能:使下一个字节地址为成为常数表达式的值。如:DSEGSEGMENTORG10VECT1DW47A5HORG20VECT2DW0C596HDSEGENDS说明:变量VECT1的偏移地址为10,即0AH,VECT2的偏移地址为20,即14H.4.3.3段定义语句语句格式:

段名SEGMENT...段名ENDS功能:定义了一个以SEGMENT伪指令开始,以ENDS伪指令结束,以段名命名的存储段。假定伪指令语句格式:

ASSUME段寄存器名:段名,段寄存器名:段名,...其中,段寄存器名必须是CS,DS,ES,SS中的一个,而段名必须是由SEGMENT定义的段中的段名。功能:设定段寄存器与段之间的关系。ASSUME语句写在代码段的一开始ASSUME语句只是指定某个段由哪一个段寄存器寻址,它并不能把段地址装入段寄存器中,所以在代码段的开始,还必须把段地址装入相应的段寄存器中。可以用MOV指令实现。但是代码段不须这样做。DATASEGMENTDATAENDSEXTRASEGMENTEXTRAENDSSTACKSEGMETSTACKENDS

............CODESEGMENTASSUMECS:CODE,DS:DATA,ES:EXTRA,SS:STACK...START:MOVAX,DATAMOVDS,AXMOVAX,EXTRAMOVES,AXMOVAX,STACK

MOVSS,AXCODEENDSENDSTART......段名也就是段地址4.3.4过程定义语句过程又称为子程序,是完成特定功能的一段程序(1)过程定义伪操作功能:定义了一个以PROC伪指令开始,以ENDP伪指令结束,以过程名命名的过程。格式:过程名procfar/near...过程名endp过程名过程名是子程序入口的符号地址(规定与标号相同,但后面不跟冒号)。(2)调用程序与子程序不在同一个代码段中,用far属性-----NEAR,FAR(1)若调用程序与子程序在同一个代码段中,用near注意:过程中若不没有注明属性,则汇编程序将它默认为NEAR。(2)过程的调用与返回以后介绍。4.3.5结束语句功能:标志着整个源程序的结束,它告诉汇编程序,没有更多的指令要汇编了。语句格式:END<程序第一条指令的标号>结束程序,返回DOS的方法一分为三个步骤(1)将程序开始启动运行的过程定义为一个FAR过程.(2)在代码段的开始处安放以下三条指令,为程序结束运行,正确返回DOS做好准备:

PUSHDSMOVAX,0PUSHAX(3)在程序结束返回DOS处安放一条RET指令结束程序,返回DOS的方法二调用DOS的4CH系统功能返回的方法在程序结束后插入以下语句:MOVAH,4CHINT21H4.4指令语句留给大家自己看。4.5汇编语言程序设计及举例4.5.1直线程序设计例1计算(V-(X*Y+Z-540))/X其中X、Y、Z、V均为16位带符号数,已分别装入X、Y、Z、V单元中,要求计算结果的商存入R单元,余数存入W单元。59DATASEGMENXDW1234HRDW?DATAENDSCODESEGMENTASSUMECS:CODE,DS:DATABEGIN:MOVAX,DATAMOVDS,AxYDW3456HZDW5678HVDW7834HWDW?60MOVAX,X;被乘数存入AXIMULY;X*YMOVCX,AX;乘积的低位字存入CXMOVBX,DX;乘积的高位字存入BXMOVAX,Z;加数Z存入AXCWD;把Z扩展成双字ADDCX,AX;乘积的低位字与Z的低位字相加ADCBX,DX;乘积与Z的高位字和低位的进位相加SUBCX,540;(X*Y+Z)的低位字减去540SBBBX,0;(X*Y+Z)的高位字减去低位的借位61MOVAX,V;被减数V存入AXCWD;把V扩展成双字SUBAX,CX;V的低位字与(X*Y+Z-540)的低位字相减SBBDX,BX;V的高位字与(X*Y+Z-540)的高位字相减IDIVX;(V-(X*Y+Z-540))/XMOVR,AXMOVW,DXMOVAH,4CHINT21HCODEENDSENDBEGIN4.1分支程序结构改变程序执行顺序、形成分支、循环、调用等程序结构是很常见的程序设计问题高级语言采用IF等语句表达条件,并根据条件是否成立转向不同的程序分支汇编语言需要首先利用比较CMP、测试TEST、加减运算、逻辑运算等影响状态标志的指令形成条件然后利用条件转移指令判断由标志表达的条件,并根据标志状态控制程序转移到不同的程序段4.1.1无条件转移指令程序代码在代码段CS:指明代码段在主存中的段基地址EIP:给出将要执行指令的偏移地址程序顺序执行,处理器自动增量EIP程序控制转移,EIP随之改变程序转移到另外的代码段,EIP和CS都改变控制转移类指令:改变EIP(有些也改变CS),即改变程序执行顺序(实现程序控制转移)的指令本章学习控制转移类指令1.转移范围段内转移在当前代码段范围内的程序转移不需更改CS,只要改变EIP(偏移地址)近转移(Near):32位近转移NEAR32,16位近转移NEAR16短转移(Short):转移范围在127~-128字节段间转移从当前代码段跳转到另一个代码段需要更改CS(段地址)和EIP(偏移地址)远转移(Far):48位远转移FAR32,32位远转移FAR162.指令寻址方式相对寻址方式提供目标地址相对于当前指令指针EIP的位移量目标地址(转移后的EIP)=当前EIP+位移量相对寻址都是段内转移,最常用、最灵活直接寻址方式直接提供目标地址目标地址(转移后的CS和EIP)=指令操作数间接寻址方式指示寄存器或存储单元目标地址来自寄存器或存储单元、间接获得寄存器间接寻址:用寄存器保存目标地址存储器间接寻址:用存储单元保存目标地址目标地址=目的地址=转移地址3.JMP指令无条件转移:程序无条件改变执行顺序JMP指令相当于高级语言的goto语句

JMPlabel ;程序转向label标号指定的地址

;段内相对寻址,段间直接寻址

JMPreg32/reg16 ;程序转向寄存器指定的地址

;寄存器间接寻址

JMPmem32/mem16 ;程序转向存储单元指定的地址

;存储器间接寻址JMP指令的4种类型1.段内转移、相对寻址标号指明目标地址,指令代码包含位移量2.段内转移、间接寻址通用寄存器或主存单元包含目标指令偏移地址3.段间转移、直接寻址标号包含目标指令的段地址和偏移地址4.段间转移、间接寻址32位段用3字存储单元包含目标地址16位段用双字存储单元包含目标地址MASM会根据存储模式等信息自动识别〔例4-1〕无条件转移程序-1

;数据段00000000 00000000nvar dword?

;代码段00000000 EB01 jmplabl1 ;相对寻址00000002 90

nop00000003 E900000001labl1: jmpnearptrlabl2 ;相对近转移00000008 90

nop00000009 B800000011Rlabl2: moveax,offsetlabl3

〔例4-1〕无条件转移程序-20000000E FFE0

jmpeax ;寄存器间接寻址00000010 90

nop00000011 B800000022Rlabl3: moveax,offsetlabl400000016 A300000000R

movnvar,eax0000001B FF2500000000R

jmpnvar ;存储器间接寻址00000021 90

noplabl4:

movebx,offsetnvarjmpnearptr[ebx];数据的寄存器间接寻址数据的存储器直接寻址4.1.2条件转移指令根据指定的条件确定程序是否发生转移

Jcclabel ;条件满足,发生转移;否则,顺序执行下条指令LABEL表示目标地址,采用段内相对寻址32位IA-32处理器:达到32位的全偏移量16位80x86处理器:-128~+127间的短转移条件转移指令不影响标志,但要利用标志cc表示利用标志判断的条件,16种、两类单个标志状态作为条件两数大小关系作为条件转移条件cc:单个标志状态JZ/JE

ZF=1 JumpifZero/EqualJNZ/JNE

ZF=0 JumpifNotZero/NotEqualJS

SF=1 JumpifSignJNS

SF=0 JumpifNotSignJP/JPE

PF=1 JumpifParity/ParityEvenJNP/JPO

PF=0 JumpifNotParity/ParityOddJO

OF=1 JumpifOverflowJNO

OF=0 JumpifNotOverflowJC

CF=1 JumpifCarryJNC

CF=0 JumpifNotCarry多个助记符方便记忆转移条件cc:两数大小关系JB/JNAE

CF=1 JumpifBelow/NotAboveorEqualJNB/JAE

CF=0 JumpifNotBelow/AboveorEqualJBE/JNA

CF=1或ZF=1 JumpifBelow/NotAboveJNBE/JA

CF=0且ZF=0 JumpifNotBeloworEqual/AboveJL/JNGE

SF≠OF JumpifLess/NotGreaterorEqualJNL/JGE

SF=OF JumpifNotLess/GreaterorEqualJLE/JNG

ZF≠OF或ZF=1 JumpifLessorEqual/NotGreaterJNLE/JG

SF=OF且ZF=0 JumpifNotLessorEqual/Greater1.单个标志状态作为条件的条件转移指令JZ(JE)和JNZ(JNE):利用零位标志ZF判断结果是零(相等)还是非零(不等)JS和JNS:利用符号标志SF判断结果是负还是正JO和JNO:利用溢出标志OF判断结果是溢出还是没有溢出JP(JPE)和JNP(JPO):利用奇偶标志PF判断结果低字节“1”的个数是偶数还是奇数JC和JNC:利用进位标志CF判断结果是有进位(为1)还是无进位(为0)〔例4-2〕个数折半程序-1

moveax,885 ;假设一个数据

shreax,1 ;数据右移进行折半

jncgoeven ;余数为0,即CF=0条件成立,转移

addeax,1 ;否则余数为1,即CF=1,进行加1操作goeven: calldispuid ;显示结果

443运行结果〔例4-2〕个数折半程序-2

moveax,886 ;假设一个数据

shreax,1 ;数据右移进行折半

jcgoodd ;余数为1,即CF=1条件成立,转移到分支体,进行加1操作

jmpgoeven ;余数为0,即CF=0,不需要处理,转移到显示!goodd: addeax,1 ;进行加1操作goeven: calldispuid ;显示结果jncgoeven何不合二为一?〔例4-2〕个数折半程序-3

moveax,887 ;假设一个数据

shreax,1 ;数据右移进行折半

adceax,0 ;余数=CF=1,进行加1操作 ;余数=CF=0,没有加1

calldispuid ;显示结果

moveax,888 ;假设一个数据

addeax,1 ;个数加1

rcreax,1 ;数据右移进行折半

calldispuid ;显示结果改进算法消除分支〔例4-3〕位测试程序—说明例如:进行打印前,要测试打印机状态。假设测试数据已经进入了EAX,其中D1位为0表示打印机没有处于联机打印的正常状态,D1 位为1表示可以进行打印。编程测试EAX,若D1=0,显示“NoReady!”,若D1=1,显示“ReadytoGo!”〔例4-3〕位测试程序-1

;数据段no_msg byte'NotReady!',0yes_msg byte'ReadytoGo!',0 ;代码段

moveax,56h ;假设一个数据

testeax,02h ;测试D1位(D1=1,其他位为0)

jznom ;D1=0条件成立,转移

moveax,offsetyes_msg ;D1=1,显示准备好

jmpdone ;跳转过另一个分支体!nom: moveax,offsetno_msg ;显示没有准备好done: calldispmsg〔例4-3〕位测试程序-2

;数据段no_msg byte'NotReady!',0yes_msg byte'ReadytoGo!',0 ;代码段

moveax,56h ;假设一个数据

testeax,02h ;测试D1位(D1=1,其他位为0)

jnzyesm ;D1=1条件成立,转移

moveax,offsetno_msg ;D1=0,显示没有准备好

jmpdone ;跳转过另一个分支体!yesm: moveax,offsetyes_msg ;显示准备好done: calldispmsg〔例4-4〕奇校验程序---说明数据通信时,为了可靠常要进行校验。最常用的方法就是奇偶校验。如果使包含校验位在内的数据中“1”的个数恒为奇数,就是奇校验;恒为偶数,就为偶校验。题目要求:编程从键盘输入一个字符,编程为其最高位加上奇校验。〔例4-4〕奇校验程序

callreadc ;键盘输入,AL=返回值

calldispcrlf ;回车换行(用于分隔)

calldispbb ;以二进制形式显示数据

calldispcrlf ;回车换行(用于分隔)

andal,7fh ;最高位置“0”、其他位不变

;同时标志PF反映“1”的个数

jnpnext ;个数为奇数,不需处理,转移

oral,80h ;个数为偶数,最高位置“1”、其他位不变next: calldispbb ;显示含校验位的数据2.两数大小关系作为条件的条件转移指令无符号数用高(Above)、低(Below)低于(不高于等于):JB(JNAE)不低于(高于等于):JNB(JAE)低于等于(不高于):JBE(JNA)不低于等于(高于):JNBE(JA)有符号数用大(Greater)、小(Less)小于(不大于等于):JL(JNGE)不小于(大于等于):JNL(JGE)小于等于(不大于):JLE(JNG)不小于等于(大于):JNLE(JG)〔例4-5〕数据比较程序-1

;数据段in_msg1 byte'Enteranumber:',0in_msg2 byte'Enteranothernumber:',0out_msg1 byte'Twonumbersareequal:',0out_msg2 byte'Thelessnumberis:',0out_msg3 byte13,10,'Thegreaternumberis:',0

;代码段

moveax,offsetin_msg1 ;提示输入

calldispmsg callreadsid ;输入第一个数据

movebx,eax ;保存到EBX

moveax,offsetin_msg2 ;提示输入

calldispmsg callreadsid ;输入第二个数据

movecx,eax ;保存到ECX〔例4-5〕数据比较程序-2

cmpebx,ecx ;二个数据进行比较

jnenequal ;两数不相等,转移

moveax,offsetout_msg1

calldispmsg ;显示两数相等

moveax,ebx calldispsid ;显示相等的数据

jmpdone ;转移到结束nequal: jlfirst ;EBX较小,不需要交换,转移

xchgebx,ecx ;EBX保存较小数,ECX保存较大数〔例4-5〕数据比较程序-3first: moveax,offsetout_msg2 ;显示较小数

calldispmsg moveax,ebx ;较小数在EBX中

calldispsid moveax,offsetout_msg3 ;显示较大数

calldispmsg

moveax,ecx ;较大数在ECX中

calldispsiddone:4.1.3单分支结构只有一个分支的程序类似高级语言的IF-THEN语句结构注意采用正确的条件转移指令当条件满足(成立),发生转移,跳过分支体条件不满足,顺序向下执行分支体条件转移指令与高级语言的IF语句正好相反IF语句是条件成立,执行分支体〔例4-6〕求绝对值程序

;代码段

callreadsid ;输入一个有符号数,从EAX返回值

cmpeax,0 ;比较EAX与0

jgenonneg ;条件满足:AX≥0,转移

negeax ;条件不满足:AX<0,为负数

;需求补得正值nonneg: calldispuid ;分支结束,显示结果示意图单分支结构的流程图返回〔例4-7〕字母判断程序

callreadc ;输入一个字符,从AL返回值

cmpal,'A’ ;与大写字母A比较

jbdone ;比大写字母A小,不是大写字母,转移

cmpal,'Z’ ;与大写字母Z比较

jadone ;比大写字母Z大,不是大写字母,转移

oral,20h ;转换为小写

calldispcrlf ;回车换行

calldispc ;显示小写字母done:

4.1.4双分支结构双分支程序结构有两个分支,条件为真执行一个分支;条件为假,执行另一个分支相当于高级语言的IF-THEN-ELSE语句顺序执行的分支体1最后一定要有一条JMP指令跳过分支体2JMP指令必不可少,实现结束前一个分支回到共同的出口作用双分支结构有时可以改变为单分支结构事先执行其中一个分支(选择出现概率较高的分支)〔例4-8〕显示数据最高位程序-1

;数据段dvar dword0bd630422h ;假设一个数据

;代码段

movebx,dvar shlebx,1 ;EBX最高位移入CF标志

jcone ;CF=1,即最高位为1,转移

moval,'0' ;CF=0,即最高位为0:AL←'0'

jmptwo ;一定要跳过另一个分支one: moval,'1' ;AL←'1'two: calldispc ;显示

示意图双分支结构双分支结构的流程图返回〔例4-8〕显示数据最高位程序-2

movebx,dvar moval,'0' ;假设最高位为0:AL←'0'

shlebx,1 ;EBX最高位移入CF标志

jnctwo ;CF=0,即最高位为0,转移

moval,'1' ;CF=1,即最高位为1,AL←'1'two: calldispc ;显示单分支结构〔例4-9〕有符号数运算溢出程序

;数据段dvar1 dword1234567890 ;假设两个数据dvar2 dword-999999999dvar3 dword?okmsg byte'Correct!',0 ;正确信息errmsg byte'ERROR!Overflow!',0 ;错误信息

;代码段

moveax,dvar1 subeax,dvar2 ;求差

joerror ;有溢出,转移

movdvar3,eax ;无溢出,保存差值

moveax,offsetokmsg ;显示正确

jmpdisperror: moveax,offseterrmsg ;显示错误disp: calldispmsg

4.2循环程序结构三个部分组成:循环初始——为开始循环准备必要的条件,如循环次数、循环体需要的初始值等;循环体——重复执行的程序代码,其中包括对循环条件的修改等;循环控制——判断循环条件是否成立,决定是否继续循环“先判断、后循环”的循环程序结构对应高级语言的WHILE语句“先循环、后判断”的循环程序结构对应高级语言的DO语句示意图循环程序结构的流程图返回4.2.1循环指令

LOOPlabel;ECX←ECX-1;若ECX≠0,循环到LABEL;否则,顺序执行

JECXZlabel;ECX=0,转移;否则顺序执行目标地址采用相对短转移实地址存储模型使用CX作为计数器DECECXJNZlabelCMPECX,0JZlabel〔例4-10〕数组求和程序;数据段Arraydword136,-138,133,130,-161Sumdword?〔例4-10〕数组求和程序

movecx,lengthofarray ;ECX=数组元素个数

xoreax,eax ;求和初值为0

movebx,eax ;数组指针为0again: addeax,array[ebx*(typearray)] ;求和

incebx ;指向下一个数组元素

loopagain movsum,eax ;保存结果

calldispsid ;显示结果循环体循环控制循环初始4.2.2计数控制循环通过次数控制循环利用LOOP指令属于计数控制常见是“先循环、后判断”循环结构计数可以减量进行,即减到0结束计数可以增量进行,即达到规定值结束循环程序结构的关键是如何控制循环〔例4-11〕简单加密解密程序-1

;数据段key byte234bufnum =255buffer bytebufnum+1dup(0) ;定义键盘输入需要的缓冲区msg1 byte'Entermessage:',0msg2 byte'Encryptedmessage:',0msg3 byte13,10,'Originalmessage:',0 ;代码段

moveax,offsetmsg1 ;提示输入字符串

calldispmsg moveax,offsetbuffer ;设置入口参数EAX

callreadmsg ;调用输入字符串子程序

pusheax ;字符个数保存进入堆栈〔例4-11〕简单加密解密程序-2

movecx,eax ;ECX=实际输入的字符个数,作为循环的次数

xorebx,ebx ;EBX指向输入字符

moval,key ;AL=加密关键字encrypt: xorbuffer[ebx],al ;异或加密

incebx dececx ;等同于指令:loopencrypt

jnzencrypt ;处理下一个字符

moveax,offsetmsg2 calldispmsg moveax,offsetbuffer ;显示密文

calldispmsg〔例4-11〕简单加密解密程序-3

popecx ;从堆栈弹出字符个数,作为循环的次数

xorebx,ebx ;EBX指向输入字符

moval,key ;AL=解密关键字decrypt: xorbuffer[ebx],al ;异或解密

incebx dececx jnzdecrypt ;处理下一个字符

moveax,offsetmsg3 calldispmsg moveax,offsetbuffer ;显示明文

calldispmsg示意图简单加密解密程序运行实例返回4.2.3条件控制循环根据条件决定是否进行循环需要使用有条件转移指令实现多见“先判断、后循环”结构先行判断的条件控制循环程序很像双分支结构主要分支需要重复执行多次(JMP的目标位置是循环开始)另一个分支用于跳出这个循环先行循环的条件控制循环程序类似单分支结构,循环体就是分支体顺序执行就跳出循环〔例4-12〕字符个数统计程序题目说明:已知某个字符串以0结尾,统计其包含的字符个数,即计算字符串的长度。〔例4-12〕字符个数统计程序 ;数据段string byte‘DoyouhavefunwithAssembly?’,0 ;以0结尾的字符串 ;代码段

xorebx,ebx ;EBX用于记录字符个数,也用于指向字符的指针again: moval,string[ebx] cmpal,0 ;用指令“testal,al”也可

jzdone incebx ;个数加1

jmpagain ;继续循环done: moveax,ebx ;显示个数

calldispuid〔例4-13〕字符剔除程序题目说明现有一个以0结尾的字符串,要求剔除其中的空格字符。处理方法:

1、用结尾标志0作为循环控制条件

2、循环体判断每个字符,如果不是空格,不予处理继续循环;如果是空格,则进行剔除,也就是将后续所有字符逐个前移一个字符位置,将空格覆盖。这是一个双重循环的程序结构。〔例4-13〕字符剔除程序-1Stringbyte‘Letushaveatry!’0dh,0ah,0’moveax,offsetstring ;显示处理前字符串

calldispmsg movesi,offsetstringoutlp: cmpbyteptr[esi],0 ;外循环,先判断后循环

jzdone ;为0结束again: cmpbyteptr[esi],'';是否是空格

jnznext ;不是空格继续循环

movedi,esi ;是空格,剔除空格分支inlp: incedi ;该分支是循环程序

moval,[edi] ;前移一个位置

mov[edi-1],al〔例4-13〕字符剔除程序-2

cmpbyteptr[edi],0

;内循环,先循环后判断

jnzinlp ;内循环结束处

jmpagain ;再次判断是否为空格(处理连续空格)next: incesi ;继续对后续字符进行判断处理

jmpoutlp ;外循环结束处done: moveax,offsetstring ;显示处理后字符串

calldispmsg4.3子程序结构经常用到的应用问题编写成一个通用子程序大型处理过程分解成能够解决的模块使用子程序可以使程序的结构更为清楚程序的维护更为方便有利于大程序开发时的多个程序员分工合作子程序(Subroutine)=函数(Function)=过程(Procedure)4.3.1子程序指令子程序:与主程序分开的、完成特定功能的一段程序当主程序(调用程序)执行调用指令CALL调用子程序子程序(被调用程序)执行返回指令RET返回主程序CALLlabel主程序RET子程序回到CALL指令后的指令处1.子程序调用指令CALLCALL指令用在主程序中,实现子程序的调用分成段内调用(近调用)和段间调用(远调用)目标地址采用相对寻址、直接寻址或间接寻址入栈返回地址:将CALL下条指令的地址压入堆栈

CALLlabel ;调用标号指定的子程序

CALLreg16/reg32

;调用寄存器指定地址的子程序

CALLmem16/mem32 ;调用存储单元指定地址的子程序2.子程序返回指令RETRET指令用在子程序结束,实现返回主程序

RET

;无参数返回:出栈返回地址

RETi16 ;有参数返回:出栈返回地址

;ESP←ESP+i16MASM会根据存储模型等信息确定子程序的远近调用,并相应产生返回指令3.过程定义伪指令MASM利用过程定义伪指令获得子程序信息

过程名 PROC

…… ;过程体

过程名 ENDP

;过程名为符合语法的标识符PROC后面可加参数:NEAR或FAR简化段定义源程序格式中,通常不需指定〔例4-14〕子程序调用程序-1

;代码段,主程序00000000 B800000001 moveax,100000005 BD00000005 movebp,50000000A E800000016

callsubp ;子程序调用0000000F B900000003

retp1: movecx,300000014 BA00000004retp2: movedx,400000019 E800000000E

calldisprd〔例4-14〕子程序调用程序-2 ;子程序subp proc ;过程定义,过程名为subp

pushebp movebp,esp movesi,[ebp+4] ;ESI=CALL下条指令(标号RETP1)偏移地址

movedi,offsetretp2 ;EDI=标号RETP2的偏移地址

movebx,2 popebp ;弹出堆栈,保持堆栈平衡

ret ;子程序返回subp endp ;过程结束MOV[EBP+4],EDI?示意图子程序调用的堆栈返回4.3.2子程序设计子程序的编写方法与主程序一样但需要留意几个问题:利用过程定义,获得子程序名和调用属性RET指令返回主程序,CALL指令调用子程序压入和弹出操作要成对使用,保持堆栈平衡开始保护寄存器,返回前相应恢复安排在代码段的主程序之外子程序允许嵌套和递归最好有完整的注释难点是参数传递〔例4-15〕十六进制显示程序题目说明将每个16进制数位转换为ASCII码。4位2进制数对应一位16进制数,具有16个数码:0-9,A-F。依次对应的ASCII码是30H-39H,41H-46H。所以,16进制数0-9只要加30H就可以转换成ASCII码,而对A-F需要再加7。HTOASC:主程序通过AL的低4位将要转换的16进制数位传递给子程序,子程序转换后的ASCII码通过AL反馈给主程序。〔例4-15〕十六进制显示程序-1.dataregd byte'EAX=',8dup(0),'H',0.code

moveax,1234abcdh ;假设一个数据

xorebx,ebx movecx,8 ;8位十六进制数again: roleax,4 ;高4位循环移位进入低4位

pusheax callhtoasc ;调用子程序

movregd+4[ebx],al ;保存转换后的ASCII码

popeax incebx dececx jnzagain moveax,offsetregd calldispmsg ;显示regdbyte'EAX=',8dup(0),'H',0〔例4-15〕十六进制显示程序-2

;子程序htoasc proc;将AL低4位表达的一位十六进制数转换为ASCII码

andal,0fh ;只取AL的低4位

oral,30h ;AL高4位变成3

cmpal,39h ;是0~9,还是A~F

jbehtoend addal,7 ;是A~F,ASCII码再加上7htoend: ret ;子程序返回htoasc endp〔例4-15〕十六进制显示程序-3

;子程序htoasc proc andeax,0fh ;取AL低4位

moval,ASCII[eax];换码

ret ;子程序的局部数据(只读)ASCII byte'0123456789ABCDEF'htoasc endpEAX=1234ABCDH运行结果4.3.3参数传递主程序与子程序间通过参数传递建立联系入口参数(输入参数):主程序→子程序出口参数(输出参数):子程序→主程序传递参数的多少反映程序模块间的耦合程度参数的具体内容数据本身(传递数值)数据的存储地址(传递地址,传递引用)参数传递方法寄存器变量堆栈1.寄存器传递参数最简单和常用的参数传递方法把参数存于约定的寄存器少量数据直接传递数值大量数据只能传递地址带有出口参数的寄存器不能保护和恢复带有入口参数的寄存器可以保护、也可以不保护,但最好能够保持一致〔例4-16〕有符号十进制数显示程序-1转换的算法如下:(1)首先判断数据是零、正数或负数,是零显示“0”退出(2)是负数,显示负号“-”,求数据的绝对值(3)接着数据除以10,余数为十进制数码,加30H转换为ASCII码保存(4)重复(3)步,直到商为0结束(5)依次从高位开始显示各位数字〔例4-16〕有符号十进制数显示程序-2

;数据段array dword1234567890,-1234,0,1,...writebuf byte12dup(0) ;显示缓冲区

;代码段

movecx,lengthofarray movebx,0again: moveax,array[ebx*4] ;EAX=入口参数

callwrite ;调用子程序,显示一个数据

calldispcrlf ;换行以便显示下一个数据

incebx dececx jnzagain寄存器传递参数〔例4-16〕有符号十进制数显示程序-3 ;显示有符号十进制数的子程序write proc ;EAX=入口参数

pushebx ;保护寄存器

pushecx pushedx movebx,offsetwritebuf;EBX指向显示缓冲区

testeax,eax ;判断数据是零、正数或负数

jnzwrite1 ;不是零,跳转

movbyteptr[ebx],'0' ;是零,设置“0”

incebx jmpwrite5 ;转向显示write1: jnswrite2 ;是正数,跳转

movbyteptr[ebx],'-';是负数,设置负号

incebx negeax ;数据求补(绝对值)寄存器传递参数〔例4-16〕有符号十进制数显示程序-4write2: movecx,10 pushecx ;10压入堆栈,作为退出标志write3: cmpeax,0 ;数据(商)为零,转向保存

jzwrite4 xoredx,edx ;零位扩展被除数为EDX.EAX

divecx ;数据除以10:EDX.EAX÷10

addedx,30h ;余数(0~9)转换为ASCII码

pushedx ;数据先低位后高位压入堆栈

jmpwrite3〔例4-16〕有符号十进制数显示程序-5write4: popedx ;数据先高位后低位弹出堆栈

cmpedx,ecx ;是结束标志10,转向显示

jewrite5 mov[ebx],dl ;数据保存到缓冲区

incebx jmpwrite4write5: movbyteptr[ebx],0 ;显示内容加上结尾标志

moveax,offsetwritebuf calldispmsg

popedx ;恢复寄存器

popecx popebx ret ;子程序返回write endp2.共享变量传递参数子程序和主程序使用同一个变量名存取数据如果变量定义和使用不在同一个程序模块中,需要利用PUBLIC、EXTREN声明共享变量传递参数,子程序的通用性较差特别适合在多个程序段间、尤其在不同的程序模块间传递数据〔例4-17〕有符号十进制数输入程序-1十进制有符号整数转换为补码的算法如下:(1)判断输入了正数、还是负数,用一个寄存器记录(2)判断下一个字符是否为有效数码 字符无效,提示错误重新输入,并转向(1) 字符有效,继续(3)字符有效,减30H转换为二进制数;然后将前面输入的数值乘10,并与刚输入的数字相加得到新的数值(4)判断输入的数据是否超出有效范围超出范围,提示错误重新输入,并转向(1)没有超出范围,则继续(5)重复(2)~(4)步,输入字符都有效,一直处理完(6)是负数进行求补,转换成补码;否则直接将数值保存〔例4-17〕有符号十进制数输入程序-2

;数据段count =10array dwordcountdup(0)temp dword?readbuf byte30dup(0) ;代码段

movecx,count movebx,offsetarrayagain: callread ;调用子程序,输入一个数据

moveax,temp ;获得出口参数

mov[ebx],eax ;存放到数据缓冲区

addebx,4 dececx jnzagain共享变量传递参数〔例4-17〕有符号十进制数输入程序-3 ;输入有符号十进制数的子程序read proc ;出口参数:变量TEMP=补码表示的二进制数值

pusheax ;说明:负数用“-”引导

pushebx

pushecx pushedxread0: moveax,offsetreadbuf

callreadmsg ;输入一个字符串

testeax,eax jzreaderr ;没有输入数据,错误

cmpeax,12 jareaderr ;输入超过12个字符,错误〔例4-17〕有符号十进制数输入程序-4

movedx,offsetreadbuf;EDX指向输入缓冲区

xorebx,ebx ;EBX保存结果

xorecx,ecx ;ECX为正负标志,0为正,-1为负

moval,[edx] ;取一个字符

cmpal,'+' ;是“+”,继续

jzread1 cmpal,'-' ;是“-”,设置-1标志

jnzread2 movecx,-1read1: incedx ;取下一个字符

moval,[edx] testal,al ;是结尾0,转向求补码

jzread3〔例4-17〕有符号十进制数输入程序-5read2: cmpal,'0' ;不是0~9之间的数码,错误

jbreaderr cmpal,'9' jareaderr subal,30h ;是0~9之间的数码,转换

imulebx,10 ;原数值乘10:EBX=EBX×10

jcreaderr ;CF=1,乘积溢出,出错

movzxeax,al ;零位扩展,便于相加

addebx,eax ;原数乘10后,与新数码相加

cmpebx,80000000h ;数据超过231,出错

jberead1 ;继续转换下一个数位〔例4-17〕有符号十进制数输入程序-6readerr: moveax,offseterrmsg ;显示出错信息

calldispmsg jmpread0 ;read3: testecx,ecx ;判断是正数还是负数

jzread4

negebx ;是负数,进行求补

jmpread5read4: cmpebx,7fffffffh ;正数超过231-1,出错

jareaderr〔例4-17〕有符号十进制数输入程序-7read5: movtemp,ebx ;设置出口参数

popedx popecx popebx popeax ret ;子程序返回errmsg byte'Inputerror,enteragain:',0read endp共享变量传递参数3.堆栈传递参数主程序将入口参数压入堆栈,子程序从堆栈中取出参数出口参数通常不使用堆栈传递高级语言进行函数调用时提供的参数,实质也利用堆栈传递采用堆栈传递参数是程式化的,它是编译程序处理参数传递、以及汇编语言与高级语言混合编程时的常规方法〔例4-18〕计算有符号数平均值程序-1

;数据段array dword675,354,-34,... ;代码段

pushlengthofarray ;压入数据个数

pushoffsetarray ;压数组的偏移地址

callmean ;调用求平均值子程序

;出口参数:EAX=平均值(整数部分)

addesp,8 ;平衡堆栈(压入了8个字节数据)

calldispsid ;显示堆栈传递参数〔例4-18〕计算有符号数平均值程序-2

;计算32位有符号数平均值子程序mean proc ;入口参数:顺序压入数据个数和数组偏移地址

pushebp ;出口参数:EAX=平均值

movebp,esp

pushebx ;保护寄存器

pushecx pushedx movebx,[ebp+8] ;EBX=取出的偏移地址

movecx,[ebp+12] ;ECX=取出的数据个数

xoreax,eax ;EAX保存和值

xoredx,edx ;EDX=指向数组元素堆栈传递参数〔例4-18〕计算有符号数平均值程序-3mean1: addeax,[ebx+edx*4] ;求和

addedx,1 ;指向下一个数据

cmpedx,ecx ;比较个数

jbmean1 ;循环

cdq ;将累加和EAX符号扩展到EDX

idivecx ;有符号数除法,EAX=平均值

popedx ;恢复寄存器

popecx popebx popebp retmean endp示意图求和溢出与个数为0的问题?利用堆栈传递参数返回4.3.4程序模块程序分段、子程序等是进行程序模块化开发大型程序时采用的方法子程序模块子程序库库文件包含宏汇编源文件包含1.子程序模块子程序单独编写,汇编形成目标模块OBJ文件连接时输入子程序模块文件名用共用伪指令PUBLIC和外部伪指令EXTERN声明PUBLIC标识符[,标识符…] ;定义标识符的模块使用EXTERN标识符:类型[,标识符:类型…] ;调用标识符的模块使用子程序在代码段,与主程序文件采用相同的存储模型,没有开始执行和结束执行点处理好子程序与主程序之间的参数传问题〔例4-19〕数据输入输出程序-1;eg0419s.asm(子程序文件)

includeio32.incpublic read,write,mean ;子程序共用extern temp:dword ;外部变量

.data ;定义的变量集中起来writebuf byte12dup(0) ;显示缓冲区readbuf byte30dup(0) .code ;代码段write procc ;明确采用C语言规范

…… ;输出子程序read pro

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论