版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第 4 章汇编语言程序设计4.1 分支程序结构4.2 循环程序结构4.3 子程序结构4.4 Windows应用程序编程4.5 与C+语言的混合编程4.1 分支程序结构改变程序执行顺序、形成分支、循环、调用等程序结构是很常见的程序设计问题高级语言采用IF等语句表达条件,并根据条件是否成立转向不同的程序分支汇编语言需要首先利用比较CMP、测试TEST、加减运算、逻辑运算等影响状态标志的指令形成条件然后利用条件转移指令判断由标志表达的条件,并根据标志状态控制程序转移到不同的程序段4.1.1 无条件转移指令程序代码在代码段CS:指明代码段在主存中的段基地址EIP:给出将要执行指令的偏移地址程序顺序执行
2、,处理器自动增量EIP程序控制转移,EIP随之改变程序转移到另外的代码段,EIP和CS都改变控制转移类指令:改变EIP(有些也改变CS),即改变程序执行顺序(实现程序控制转移)的指令本章学习控制转移类指令1. 转移范围段内转移在当前代码段范围内的程序转移不需更改CS,只要改变EIP(偏移地址)近转移(Near):32位近转移NEAR32,16位近转移NEAR16短转移(Short):转移范围在127-128字节段间转移从当前代码段跳转到另一个代码段需要更改CS(段地址)和EIP(偏移地址)远转移(Far): 48位远转移FAR32,32位远转移FAR162. 指令寻址方式相对寻址方式提供目标地
3、址相对于当前指令指针EIP的位移量目标地址(转移后的EIP)当前EIP位移量相对寻址都是段内转移,最常用、最灵活直接寻址方式直接提供目标地址目标地址(转移后的CS和EIP)指令操作数间接寻址方式指示寄存器或存储单元目标地址来自寄存器或存储单元、间接获得寄存器间接寻址:用寄存器保存目标地址存储器间接寻址:用存储单元保存目标地址目标地址目的地址转移地址3. JMP指令无条件转移:程序无条件改变执行顺序JMP指令相当于高级语言的goto语句JMP label;程序转向label标号指定的地址;段内相对寻址,段间直接寻址JMP reg32/reg16;程序转向寄存器指定的地址;寄存器间接寻址JMP m
4、em48/mem32/mem16;程序转向存储单元指定的地址;存储器间接寻址JMP指令的4种类型1. 段内转移、相对寻址标号指明目标地址,指令代码包含位移量2. 段内转移、间接寻址通用寄存器或主存单元包含目标指令偏移地址3. 段间转移、直接寻址标号包含目标指令的段地址和偏移地址4. 段间转移、间接寻址32位段用3字存储单元包含目标地址16位段用双字存储单元包含目标地址MASM会根据存储模式等信息自动识别例4-1无条件转移程序1; 数据段0000000000000000nvardword ? ; 代码段00000000EB 01jmp labl1; 相对寻址0000000290nop000000
5、03E9 00000001labl1:jmp near ptr labl2; 相对近转移0000000890nop00000009B8 00000011 Rlabl2:mov eax,offset labl3 例4-1无条件转移程序20000000EFF E0jmp eax; 寄存器间接寻址0000001090nop00000011B8 00000022 Rlabl3:mov eax,offset labl400000016A3 00000000 Rmov nvar,eax0000001BFF 25 00000000 Rjmp nvar; 存储器间接寻址0000002190noplabl4:
6、mov ebx,offset nvarjmp near ptr ebx;数据的寄存器间接寻址数据的存储器直接寻址4.1.2 条件转移指令根据指定的条件确定程序是否发生转移Jcc label;条件满足,发生转移;否则,顺序执行下条指令LABEL表示目标地址,采用段内相对寻址32位IA-32处理器:达到32位的全偏移量16位80 x86处理器:-128+127间的短转移条件转移指令不影响标志,但要利用标志cc表示利用标志判断的条件,16种、两类单个标志状态作为条件两数大小关系作为条件转移条件cc:单个标志状态JZ/JEZF=1Jump if Zero/EqualJNZ/JNEZF=0Jump if
7、 Not Zero/Not EqualJSSF=1Jump if SignJNSSF=0Jump if Not SignJP/JPEPF=1Jump if Parity/Parity EvenJNP/JPOPF=0Jump if Not Parity/Parity OddJOOF=1Jump if OverflowJNOOF=0Jump if Not OverflowJCCF=1Jump if CarryJNCCF=0Jump if Not Carry多个助记符方便记忆转移条件cc:两数大小关系JB/JNAECF=1Jump if Below/Not Above or EqualJNB/JAE
8、CF=0Jump if Not Below/Above or EqualJBE/JNACF=1或ZF=1Jump if Below/Not AboveJNBE/JACF=0且ZF=0Jump if Not Below or Equal/AboveJL/JNGESFOFJump if Less/Not Greater or EqualJNL/JGESF=OFJump if Not Less/Greater or EqualJLE/JNGSFOF或ZF=1Jump if Less or Equal/Not GreaterJNLE/JGSF=OF且ZF=0Jump if Not Less or Eq
9、ual/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个数折半程序1mov eax,885; 假设一个数据shr eax,1; 数据右移进行折半jnc goeven; 余数为0,即CF0条件成立,转移add eax,1; 否则余数为1
10、,即CF1,进行加1操作goeven:call dispuid; 显示结果 443运行结果例4-2个数折半程序2mov eax,886; 假设一个数据shr eax,1; 数据右移进行折半jc goodd; 余数为1,即CF1条件成立,转移到分支体,进行加1操作jmp goeven; 余数为0,即CF0,不需要处理,转移到显示!goodd:add eax,1; 进行加1操作goeven:call dispuid; 显示结果jnc goeven 何不合二为一?例4-2个数折半程序3mov eax,887; 假设一个数据shr eax,1; 数据右移进行折半adc eax,0; 余数CF1,进行加
11、1操作;余数CF0,没有加1call dispuid; 显示结果mov eax,888; 假设一个数据add eax,1; 个数加1rcr eax,1; 数据右移进行折半call dispuid; 显示结果改进算法消除分支例4-3位测试程序1; 数据段no_msgbyte Not Ready!,0yes_msgbyte Ready to Go!,0; 代码段mov eax,56h; 假设一个数据test eax,02h; 测试D1位(D11,其他位为0)jz nom; D10条件成立,转移mov eax,offset yes_msg; D11,显示准备好jmp done; 跳转过另一个分支体!
12、nom:mov eax,offset no_msg; 显示没有准备好done:call dispmsg例4-3位测试程序2; 数据段no_msgbyte Not Ready!,0yes_msgbyte Ready to Go!,0; 代码段mov eax,56h; 假设一个数据test eax,02h; 测试D1位(D11,其他位为0)jnz yesm; D11条件成立,转移mov eax,offset no_msg; D10,显示没有准备好jmp done; 跳转过另一个分支体!yesm:mov eax,offset yes_msg; 显示准备好done:call dispmsg例4-4奇校
13、验程序call readc; 键盘输入, AL返回值call dispcrlf; 回车换行(用于分隔)call dispbb; 以二进制形式显示数据call dispcrlf; 回车换行(用于分隔)and al,7fh; 最高位置“0”、其他位不变; 同时标志PF反映“1”的个数jnp next; 个数为奇数,不需处理,转移or al,80h; 个数为偶数,最高位置“1”、其他位不变 next:call dispbb; 显示含校验位的数据2. 两数大小关系作为条件的条件转移指令无符号数用高(Above)、低(Below)低于(不高于等于):JB(JNAE)不低于(高于等于):JNB(JAE)低
14、于等于(不高于):JBE(JNA)不低于等于(高于):JNBE(JA)有符号数用大(Greater)、小(Less)小于(不大于等于):JL(JNGE)不小于(大于等于):JNL(JGE)小于等于(不大于):JLE(JNG)不小于等于(大于):JNLE(JG)例4-5数据比较程序1; 数据段in_msg1byte Enter a number: ,0in_msg2byte Enter another number: ,0out_msg1byte Two numbers are equal: ,0out_msg2byte The less number is: ,0out_msg3byte 13
15、,10,The greater number is: ,0; 代码段mov eax,offset in_msg1; 提示输入call dispmsgcall readsid; 输入第一个数据mov ebx,eax; 保存到EBXmov eax,offset in_msg2; 提示输入call dispmsgcall readsid; 输入第二个数据mov ecx,eax; 保存到ECX例4-5数据比较程序2cmp ebx,ecx; 二个数据进行比较jne nequal; 两数不相等,转移mov eax,offset out_msg1call dispmsg; 显示两数相等mov eax,ebx
16、call dispsid; 显示相等的数据jmp done; 转移到结束nequal:jl first; EBX较小,不需要交换,转移xchg ebx,ecx; EBX保存较小数,ECX保存较大数例4-5数据比较程序3first:mov eax,offset out_msg2; 显示较小数call dispmsgmov eax,ebx; 较小数在EBX中call dispsidmov eax,offset out_msg3; 显示较大数call dispmsgmov eax,ecx; 较大数在ECX中call dispsid done:4.1.3 单分支结构只有一个分支的程序类似高级语言的IF
17、-THEN语句结构注意采用正确的条件转移指令当条件满足(成立),发生转移,跳过分支体条件不满足,顺序向下执行分支体条件转移指令与高级语言的IF语句正好相反IF语句是条件成立,执行分支体例4-6求绝对值程序; 代码段call readsid; 输入一个有符号数,从EAX返回值cmp eax,0; 比较EAX与0jge nonneg; 条件满足:EAX0,转移neg eax; 条件不满足:EAX0,为负数; 需求补得正值nonneg:call dispuid;分支结束,显示结果示意图单分支结构的流程图返回neg eax例4-7字母判断程序call readc; 输入一个字符,从AL返回值cmp a
18、l,A; 与大写字母A比较jb done; 比大写字母A小,不是大写字母,转移cmp al,Z; 与大写字母Z比较ja done; 比大写字母Z大,不是大写字母,转移or al,20h; 转换为小写call dispcrlf; 回车换行call dispc; 显示小写字母done: 4.1.4 双分支结构双分支程序结构有两个分支,条件为真执行一个分支;条件为假,执行另一个分支相当于高级语言的IF-THEN-ELSE语句顺序执行的分支体1最后一定要有一条JMP指令跳过分支体2JMP指令必不可少,实现结束前一个分支回到共同的出口作用双分支结构有时可以改变为单分支结构事先执行其中一个分支(选择出现概
19、率较高的分支) 例4-8显示数据最高位程序1; 数据段dvardword 0bd630422h; 假设一个数据; 代码段mov ebx,dvarshl ebx,1; EBX最高位移入CF标志jc one; CF1,即最高位为1,转移mov al,0; CF0,即最高位为0:AL0jmp two; 一定要跳过另一个分支one:mov al,1; AL1two:call dispc; 显示 示意图双分支结构双分支结构的流程图返回例4-8显示数据最高位程序2mov ebx,dvarmov al,0; 假设最高位为0:AL0shl ebx,1; EBX最高位移入CF标志jnc two; CF0,即最高
20、位为0,转移mov al,1; CF1,即最高位为1,AL1two:call dispc; 显示单分支结构例4-9有符号数运算溢出程序; 数据段dvar1dword 1234567890; 假设两个数据dvar2dword -999999999dvar3dword ?okmsgbyte Correct!,0; 正确信息errmsgbyte ERROR ! Overflow!,0; 错误信息; 代码段mov eax,dvar1 sub eax,dvar2; 求差jo error; 有溢出,转移mov dvar3,eax; 无溢出,保存差值mov eax,offset okmsg; 显示正确jmp
21、 disperror:mov eax,offset errmsg; 显示错误disp:call dispmsg 4.2 循环程序结构三个部分组成:循环初始为开始循环准备必要的条件,如循环次数、循环体需要的初始值等;循环体重复执行的程序代码,其中包括对循环条件的修改等;循环控制判断循环条件是否成立,决定是否继续循环“先判断、后循环”的循环程序结构对应高级语言的WHILE语句“先循环、后判断”的循环程序结构对应高级语言的DO语句示意图循环程序结构的流程图返回4.2.1 循环指令LOOP label;ECXECX1;若ECX0,循环到LABEL;否则,顺序执行JECXZ label;ECX0,转移;
22、否则顺序执行目标地址采用相对短转移实地址存储模型使用CX作为计数器DEC ECXJNZ labelCMP ECX,0JZ label例4-10数组求和程序mov ecx,lengthof array; ECX数组元素个数xor eax,eax; 求和初值为0mov ebx,eax; 数组指针为0again:add eax,arrayebx*(type array); 求和 inc ebx; 指向下一个数组元素loop againmov sum,eax; 保存结果call dispsid; 显示结果循环体循环控制循环初始4.2.2 计数控制循环通过次数控制循环利用LOOP指令属于计数控制常见是“
23、先循环、后判断”循环结构计数可以减量进行,即减到0结束计数可以增量进行,即达到规定值结束循环程序结构的关键是如何控制循环例4-11简单加密解密程序1; 数据段keybyte 234bufnum= 255bufferbyte bufnum+1 dup(0); 定义键盘输入需要的缓冲区msg1byte Enter messge: ,0msg2byte Encrypted message: ,0msg3byte 13,10,Original messge: ,0; 代码段mov eax,offset msg1; 提示输入字符串call dispmsgmov eax,offset buffer; 设置
24、入口参数EAXcall readmsg; 调用输入字符串子程序push eax; 字符个数保存进入堆栈例4-11简单加密解密程序2mov ecx,eax; ECX实际输入的字符个数,作为循环的次数xor ebx,ebx; EBX指向输入字符mov al,key; AL加密关键字encrypt:xor bufferebx,al; 异或加密inc ebxdec ecx; 等同于指令:loop encryptjnz encrypt; 处理下一个字符mov eax,offset msg2call dispmsgmov eax,offset buffer; 显示密文call dispmsg例4-11简单
25、加密解密程序3pop ecx; 从堆栈弹出字符个数,作为循环的次数xor ebx,ebx; EBX指向输入字符mov al,key; AL解密关键字decrypt:xor bufferebx,al; 异或解密inc ebxdec ecxjnz decrypt; 处理下一个字符mov eax,offset msg3call dispmsgmov eax,offset buffer; 显示明文call dispmsg示意图简单加密解密程序运行实例返回4.2.3 条件控制循环根据条件决定是否进行循环需要使用有条件转移指令实现多见“先判断、后循环”结构先行判断的条件控制循环程序很像双分支结构主要分支需
26、要重复执行多次(JMP的目标位置是循环开始)另一个分支用于跳出这个循环先行循环的条件控制循环程序类似单分支结构,循环体就是分支体顺序执行就跳出循环例4-12字符个数统计程序; 数据段stringbyte Do you have fun with Assembly?,0; 以0结尾的字符串; 代码段xor ebx,ebx; EBX用于记录字符个数,也用于指向字符的指针again:mov al,stringebxcmp al,0; 用指令“test al,al”更好jz doneinc ebx; 个数加1jmp again; 继续循环done:mov eax,ebx; 显示个数call dispu
27、id例4-13字符剔除程序1mov eax,offset string; 显示处理前字符串call dispmsgmov esi,offset stringoutlp:cmp byte ptr esi,0; 外循环,先判断后循环jz done; 为0结束again:cmp byte ptr esi, ; 是否是空格jnz next; 不是空格继续循环mov edi,esi; 是空格,剔除空格分支inlp:inc edi; 该分支是循环程序mov al,edi; 前移一个位置mov edi-1,al例4-13字符剔除程序2cmp byte ptr edi,0; 内循环,先循环后判断jnz inl
28、p; 内循环结束处jmp again; 再次判断是否为空格(处理连续空格)next:inc esi; 继续对后续字符进行判断处理jmp outlp; 外循环结束处done:mov eax,offset string; 显示处理后字符串call dispmsg4.3 子程序结构经常用到的应用问题编写成一个通用子程序大型处理过程分解成能够解决的模块使用子程序可以使程序的结构更为清楚程序的维护更为方便有利于大程序开发时的多个程序员分工合作子程序(Subroutine)函数(Function)过程(Procedure)4.3.1 子程序指令子程序:与主程序分开的、完成特定功能的一段程序当主程序(调用程
29、序)执行调用指令CALL调用子程序子程序(被调用程序)执行返回指令RET返回主程序CALL label主程序RET子程序回到CALL指令后的指令处1. 子程序调用指令CALLCALL指令用在主程序中,实现子程序的调用分成段内调用(近调用)和段间调用(远调用)目标地址采用相对寻址、直接寻址或间接寻址入栈返回地址:将CALL下条指令的地址压入堆栈CALL label;调用标号指定的子程序CALL reg16/reg32;调用寄存器指定地址的子程序CALL mem16/mem32/mem48;调用存储单元指定地址的子程序2. 子程序返回指令RETRET指令用在子程序结束,实现返回主程序RET;无参数
30、返回:出栈返回地址RET i16;有参数返回:出栈返回地址;ESPESPi16MASM会根据存储模型等信息确定子程序的远近调用,并相应产生返回指令3. 过程定义伪指令MASM利用过程定义伪指令获得子程序信息 过程名PROC;过程体过程名ENDP;过程名为符合语法的标识符PROC后面可加参数:NEAR或FAR简化段定义源程序格式中,通常不需指定例4-14子程序调用程序1; 代码段,主程序00000000B8 00000001mov eax,100000005BD 00000005mov ebp,50000000AE8 00000016call subp; 子程序调用0000000FB9 0000
31、0003retp1:mov ecx,300000014BA 00000004retp2:mov edx,400000019E8 00000000 Ecall disprd例4-14子程序调用程序2; 子程序subpproc; 过程定义,过程名为subppush ebpmov ebp,espmov esi,ebp+4; ESICALL下条指令(标号RETP1)偏移地址mov edi,offset retp2; EDI标号RETP2的偏移地址mov ebx,2pop ebp; 弹出堆栈,保持堆栈平衡ret; 子程序返回subpendp; 过程结束MOV EBP+4,EDI?示意图子程序调用的堆栈返
32、回4.3.2 子程序设计子程序的编写方法与主程序一样但需要留意几个问题:利用过程定义,获得子程序名和调用属性RET指令返回主程序,CALL指令调用子程序压入和弹出操作要成对使用,保持堆栈平衡开始保护寄存器,返回前相应恢复安排在代码段的主程序之外子程序允许嵌套和递归最好有完整的注释难点是参数传递回车换行子程序DPCRLFdpcrlfproc;回车换行子程序push eax;保护寄存器mov al,0dh;输出回车字符call dispc;子程序中调用子程序,实现子程序嵌套mov al,0ah;输出换行字符call dispc;子程序中调用子程序,实现子程序嵌套pop eax;恢复寄存器ret;子
33、程序返回dpcrlfendp4.3.3 参数传递主程序与子程序间通过参数传递建立联系入口参数(输入参数):主程序子程序出口参数(输出参数):子程序主程序传递参数的多少反映程序模块间的耦合程度参数的具体内容数据本身(传递数值)数据的存储地址(传递地址,传递引用)参数传递方法寄存器变量堆栈1. 寄存器传递参数最简单和常用的参数传递方法把参数存于约定的寄存器少量数据直接传递数值大量数据只能传递地址带有出口参数的寄存器不能保护和恢复带有入口参数的寄存器可以保护、也可以不保护,但最好能够保持一致例4-15十六进制显示程序1mov eax, 1234abcdh; 假设一个数据xor ebx,ebxmov
34、ecx,8; 8位十六进制数again:rol eax,4; 高4位循环移位进入低4位push eax; mov edx,eaxcall htoasc; 调用子程序mov regd+4ebx,al; 保存转换后的ASCII码pop eax; mov eax,edxinc ebxdec ecxjnz againmov eax,offset regdcall dispmsg; 显示regd byte EAX=,8 dup(0),H,0例4-15十六进制显示程序2; 子程序htoascproc; 将AL低4位表达的一位十六进制数转换为ASCII码and al,0fh; 只取AL的低4位or al,3
35、0h; AL高4位变成3cmp al,39h; 是09,还是AFjbe htoendadd al,7; 是AF,ASCII码再加上7htoend:ret; 子程序返回htoascendp例4-15十六进制显示程序3; 子程序htoascproc and eax,0fh; 取AL低4位mov al,ASCIIeax ; 换码ret; 子程序的局部数据(只读)ASCIIbyte 0123456789ABCDEFhtoascendpEAX=1234ABCDH运行结果例4-16有符号十进制数显示程序1转换的算法如下:(1)首先判断数据是零、正数或负数,是零显示“0”退出(2)是负数,显示负号“”,求数
36、据的绝对值(3)接着数据除以10,余数为十进制数码,加30H转换为ASCII码保存(4)重复(3)步,直到商为0结束(5)依次从高位开始显示各位数字例4-16有符号十进制数显示程序2; 数据段arraydword 1234567890,-1234,0,1,.writebufbyte 12 dup(0); 显示缓冲区; 代码段mov ecx,lengthof arraymov ebx,0again:mov eax,arrayebx*4 ; EAX入口参数call write; 调用子程序,显示一个数据call dispcrlf; 换行以便显示下一个数据inc ebxdec ecxjnz agai
37、n寄存器传递参数例4-16有符号十进制数显示程序3; 显示有符号十进制数的子程序writeproc; EAX入口参数push ebx; 保护寄存器push ecxpush edxmov ebx,offset writebuf ; EBX指向显示缓冲区test eax,eax; 判断数据是零、正数或负数jnz write1; 不是零,跳转mov byte ptr ebx,0; 是零,设置“0”inc ebxjmp write5; 转向显示write1:jns write2; 是正数,跳转mov byte ptr ebx,- ; 是负数,设置负号inc ebxneg eax; 数据求补(绝对值)寄
38、存器传递参数例4-16有符号十进制数显示程序4write2:mov ecx,10push ecx; 10压入堆栈,作为退出标志write3:cmp eax,0; 数据(商)为零,转向保存jz write4 xor edx,edx; 零位扩展被除数为EDX.EAXdiv ecx; 数据除以10:EDX.EAX10add edx,30h; 余数(09)转换为ASCII码push edx; 数据先低位后高位压入堆栈jmp write3例4-16有符号十进制数显示程序5write4:pop edx; 数据先高位后低位弹出堆栈cmp edx,ecx; 是结束标志10,转向显示je write5mov e
39、bx,dl; 数据保存到缓冲区inc ebxjmp write4write5:mov byte ptr ebx,0; 显示内容加上结尾标志mov eax,offset writebufcall dispmsgpop edx; 恢复寄存器pop ecxpop ebxret; 子程序返回writeendp2. 共享变量传递参数子程序和主程序使用同一个变量名存取数据如果变量定义和使用不在同一个程序模块中,需要利用PUBLIC、EXTREN声明共享变量传递参数,子程序的通用性较差特别适合在多个程序段间、尤其在不同的程序模块间传递数据例4-17二进制数输入程序1输入一个字符,判断是否合法是字符“0”或“
40、1”合法减去30H转换成数值“0”或“1”重复转换每个字符将前一次的数值左移1位并与新数值进行组合输入非法字符,或超过数据位数提示错误重新输入例4-17二进制数输入程序2; 数据段count= 5arraydword count dup(0)tempdword ?; 代码段mov ecx,countmov ebx,offset arrayagain:call rdbd; 调用子程序,输入一个数据mov eax,temp; 获得出口参数mov ebx,eax; 存放到数据缓冲区add ebx,4loop again共享变量传递参数例4-17二进制数输入程序3; 输入二进制数的子程序rdbdpro
41、c; 出口参数:变量TEMP补码表示的二进制数值push eax; 说明:负数用“”引导push ebxpush ecxrdbd1:xor ebx,ebx;EBX用于存放二进制结果mov ecx,32;限制输入字符的个数rdbd2:call readc;输入一个字符cmp al,0;检测键入字符是否合法jb rderr;不合法则返回重新输入cmp al,1ja rderr例4-17二进制数输入程序4sub al,0;对输入的字符进行转化shl ebx,1;EBX的值乘以2or bl,al;BL和AL相加loop rdbd2;循环键入字符mov temp,ebx;把EBX的二进制结果存放TEMP
42、返回call dispcrlf;分行pop ecxpop ebxpop eaxret;子程序返回例4-17二进制数输入程序5rderr:mov eax,offset errmsg;显示错误信息call dispmsgjmp rdbd1errmsgbyte 0dh,0ah,Input error, enter again: ,0rdbdendp共享变量传递参数例4-18有符号十进制数输入程序1十进制有符号整数转换为补码的算法如下:(1)判断输入了正数、还是负数,用一个寄存器记录(2)判断下一个字符是否为有效数码字符无效,提示错误重新输入,并转向(1)字符有效,继续(3)字符有效,减30H转换为二
43、进制数;然后将前面输入的数值乘10,并与刚输入的数字相加得到新的数值(4)判断输入的数据是否超出有效范围超出范围,提示错误重新输入,并转向(1)没有超出范围,则继续(5)重复(2)(4)步,输入字符都有效,一直处理完(6)是负数进行求补,转换成补码;否则直接将数值保存例4-18有符号十进制数输入程序2; 数据段count= 10arraydword count dup(0)tempdword ?readbufbyte 30 dup(0); 代码段mov ecx,countmov ebx,offset arrayagain:call read; 调用子程序,输入一个数据mov eax,temp;
44、 获得出口参数mov ebx,eax; 存放到数据缓冲区add ebx,4dec ecxjnz again共享变量传递参数例4-18有符号十进制数输入程序3; 输入有符号十进制数的子程序readproc; 出口参数:变量TEMP补码表示的二进制数值push eax; 说明:负数用“”引导push ebxpush ecxpush edxread0:mov eax,offset readbufcall readmsg; 输入一个字符串test eax,eaxjz readerr; 没有输入数据,错误cmp eax,12ja readerr; 输入超过12个字符,错误例4-18有符号十进制数输入程序
45、4mov edx,offset readbuf ; EDX指向输入缓冲区xor ebx,ebx; EBX保存结果xor ecx,ecx;ECX为正负标志,0为正,1为负mov al,edx; 取一个字符cmp al,+; 是“”,继续jz read1cmp al,-; 是“”,设置1标志jnz read2mov ecx,-1read1:inc edx; 取下一个字符mov al,edxtest al,al; 是结尾0,转向求补码jz read3例4-18有符号十进制数输入程序5read2:cmp al,0; 不是09之间的数码,错误jb readerrcmp al,9ja readerrsub
46、 al,30h; 是09之间的数码,转换imul ebx,10; 原数值乘10:EBXEBX10jc readerr; CF1,乘积溢出,出错movzx eax,al; 零位扩展,便于相加add ebx,eax; 原数乘10后,与新数码相加cmp ebx,80000000h; 数据超过231,出错jbe read1; 继续转换下一个数位例4-18有符号十进制数输入程序6readerr:mov eax,offset errmsg; 显示出错信息call dispmsgjmp read0;read3:test ecx,ecx; 判断是正数还是负数jz read4neg ebx; 是负数,进行求补j
47、mp read5read4:cmp ebx,7fffffffh; 正数超过231-1,出错ja readerr例4-18有符号十进制数输入程序7read5:mov temp,ebx; 设置出口参数pop edxpop ecxpop ebxpop eaxret; 子程序返回errmsgbyte Input error, enter again: ,0readendp共享变量传递参数3. 堆栈传递参数主程序将入口参数压入堆栈,子程序从堆栈中取出参数出口参数通常不使用堆栈传递高级语言进行函数调用时提供的参数,实质也利用堆栈传递采用堆栈传递参数是程式化的,它是编译程序处理参数传递、以及汇编语言与高级语
48、言混合编程时的常规方法例4-19计算有符号数平均值程序1; 数据段arraydword 675,354,-34, .; 代码段push lengthof array; 压入数据个数push offset array; 压数组的偏移地址call mean; 调用求平均值子程序;出口参数:EAX平均值(整数部分)add esp,8; 平衡堆栈(压入了8个字节数据)call dispsid; 显示堆栈传递参数例4-19计算有符号数平均值程序2; 计算32位有符号数平均值子程序meanproc; 入口参数:顺序压入数据个数和数组偏移地址push ebp;出口参数:EAX平均值mov ebp,esppu
49、sh ebx; 保护寄存器push ecxpush edxmov ebx,ebp+8; EBX取出的偏移地址mov ecx,ebp+12; ECX取出的数据个数xor eax,eax; EAX保存和值xor edx,edx; EDX指向数组元素堆栈传递参数例4-19计算有符号数平均值程序3mean1:add eax,ebx+edx*4; 求和add edx,1; 指向下一个数据cmp edx,ecx; 比较个数jb mean1; 循环cdq; 将累加和EAX符号扩展到EDXidiv ecx; 有符号数除法,EAX平均值pop edx; 恢复寄存器pop ecxpop ebxpop ebpret
50、meanendp示意图求和溢出与个数为0的问题?利用堆栈传递参数返回4.3.4 程序模块程序分段、子程序等是进行程序模块化开发大型程序时采用的方法子程序模块子程序库库文件包含宏汇编源文件包含1. 子程序模块子程序单独编写,汇编形成目标模块OBJ文件连接时输入子程序模块文件名用共用伪指令PUBLIC和外部伪指令EXTERN声明PUBLIC 标识符 ,标识符 ;定义标识符的模块使用EXTERN 标识符:类型 ,标识符:类型 ;调用标识符的模块使用子程序在代码段,与主程序文件采用相同的存储模型,没有开始执行和结束执行点处理好子程序与主程序之间的参数传问题例4-20数据输入输出程序1; eg0420s
51、.asm(子程序文件)include io32.incpublicread,write,mean; 子程序共用externtemp:dword; 外部变量.data; 定义的变量集中起来writebufbyte 12 dup(0); 显示缓冲区readbufbyte 30 dup(0).code; 代码段writeproc c; 明确采用C语言规范; 输出子程序readproc c; 输入子程序meanproc c; 计算平均值子程序end例4-20数据输入输出程序2; eg0420.asm(主程序文件)include io32.incexternread:near,write:near,me
52、an:near; 外部子程序publictemp; 变量共用.datacount= 10arraydword count dup(0)tempdword ?msg1byte Enter 10 numbers:,13,10,0msg2byte The mean is: ,0.codestart:mov eax,offset msg1; 提示输入10个数据call dispmsg例4-20数据输入输出程序3xor ebx,ebx mov ecx,count; ECX数据个数again:call read; 调用子程序,输入一个数据mov eax,temp; 获得出口参数mov array ebx*
53、4,eax add ebx,1cmp ebx,ecxjb again例4-20数据输入输出程序4push ecx; 传递参数push offset arraycall mean; 调用子程序,求平均值add esp,8mov ebx,eax; EAX返回值转存到EBXmov eax,offset msg2; 提示输出平均值call dispmsg mov eax,ebx; 提示输出平均值call write; 调用子程序,显示平均值exit 0end start2. 子程序库子程序库:统一管理子程序模块遵循更加严格的子程序模块要求子程序文件编写完成、汇编形成目标模块利用库管理工具程序LIB.E
54、XE:把子程序模块逐个加入到子程序库(.LIB)使用子程序库:在连接主程序模块时提供子程序库文件名操作演示数据输入输出程序子程序模块数据输入输出程序执行实例数据输入输出程序子程序库END3. 库文件包含直接在主程序源文件中用库文件包含伪指令INCLUDELIB说明不用在连接时输入库文件名INCLUDELIB 文件名子程序库文件名要符合操作系统规范必要时含有路径,用于指明文件的存储位置如果没有路径名,汇编程序将在默认目录、当前目录和指定目录下寻找 4. 宏汇编宏(Macro):具有宏名的一段汇编语句序列宏需要先定义宏名MACRO 形参表;宏定义体ENDM然后程序中进行宏调用宏名 实体参数在汇编时
55、,宏指令被汇编程序用宏定义的代码序列替代,实现宏展开宏汇编示例宏定义WriteStringmacro msgpush eaxlea eax,msgcall dispmsgpop eaxendm宏展开push eaxlea eax,msgcall dispmsgpop eax宏调用WriteString msg5. 源文件包含INCLUDE 文件名将INCLUDE伪指令指定的文本文件内容插入源程序可以包含任何文本文件一些常用的或有价值的宏定义存放在.MAC宏定义文件各种常量定义、声明语句等组织在.INC包含文件常用的子程序形成.ASM汇编语言源文件利用INCLUDE伪指令包含其他文件,其实质仍然
56、是一个源程序,只不过是分在了几个文件书写组合两种文件包含、以及宏汇编等方法,可以精简程序框架、简化程序设计IO32.INCIO32.INC.nolist; 不列表内容;filename: io32.inc.;declare proceduresexternreadc:near,readmsg:nearexterndispc:near,dispmsg:near,dispcrlf:near;declare I/O librariesincludelib io32.lib;define macrosWriteString MACRO string.list; 列表内容返回4.4 Windows应用程
57、序编程汇编语言可以编写32位Windows应用程序调用Windows的应用程序接口API运行于Windows操作系统平台可以利用Windows的高级特性,生成的可执行文件相对较小、性能更高从更深层次理解Windows运行机制及程序设计思想4.4.1 操作系统函数调用操作系统以其提供的系统函数(系统功能System function)支持程序员进行编程Windows的系统函数(功能)以动态连接库DLL(Dynamic-Link Library)形式提供,利用其应用程序接口API(Application Program Interface)调用DDL库中的函数API是一些类型、常量和函数的集合,提
58、供了编程中使用库函数的途径Win16:16位Windows的APIWin32:32位Windows的API1. 动态连接库静态连接:连接程序从库文件中抽取需要的子程序插入到最终的可执行代码中动态连接:程序运行时才将代码加载到主存动态连接库:保存程序运行时需要重复使用的代码的文件3个最重要的Windows动态连接库KERNEL32.DLL:主要处理内存管理和进程调度USER32.DLL:主要控制用户界面GDI32.DLL:负责图形方面的操作导入库(Import Library):程序开发的连接阶段使用,与一个动态连接库DLL对应2. MASM的高级语言特性条件控制伪指令:.IF .ELSEIF
59、.ELSE .ENDIF流程控制伪指令:.WHILE .ENDW .REPEAT .UNTIL.REPEAT .UNTILCXZ .BREAK .CONTINUE过程声明伪指令PROTO:事先声明过程的结构(包括操作系统API函数、高级语言的函数)过程名PROTO调用距离 语言类型 ,参数类型.过程调用伪指令INVOKE:实现过程调用INVOKE过程名,参数,.3. 程序退出函数Win32程序员参考手册VOID ExitProcess( UINT uExitCode/ exit code for all threads );汇编语言声明ExitProcess PROTO ,:DWORD汇编语言
60、调用invoke ExitProcess,0将函数调用定义成宏exitMACRO dwexitcodeinvoke ExitProcess,dwexitcodeENDM宏调用exit04.4.2 控制台应用程序Windows应用程序开始运行创建控制台(Console)窗口或创建图形界面窗口32位Windows控制台程序像增强版的MS-DOS程序使用标准控制台标准输入设备(键盘)标准输出设备(显示器)32位控制台程序运行在保护方式通过API使用Windows的动态链接库函数1. 处理器识别指令CPUID返回处理器特征信息的指令当EAX0时执行CPUID指令EAX返回CPUID指令中能够赋给EAX
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026陕西西安交通大学电信学部电子学院管理辅助工作人员招聘1人备考题库【巩固】附答案详解
- 2026北京对外经济贸易大学非事业编人员招聘7人备考题库【含答案详解】
- 2026年青海高等职业技术学院单招综合素质考试题库参考答案详解
- 三年级数学学习目标与考核方案
- 曲线运动物理问题解决方案
- 建筑施工进度控制方案范文
- 中国环保设备制造业供需分析与投资规划研究报告
- 中国环保材料行业市场现状及政策支持与可持续发展前景报告
- 中国环保服务行业市场调研及竞争格局与商业机会评估分析报告
- 中国环保型商用洗涤剂政策环境与技术发展路径研究报告
- 公共卫生组织管理工作计划(31篇)
- 电厂值长培训课件
- 美妆培训行业薪酬制度
- 2026年湖南机电职业技术学院单招综合素质考试题库附答案
- 市监局规范外卖管理制度
- (正式版)DB51∕T 3326-2025 《展会现场服务规范》
- 小学劳动课《收纳》
- 食品生产加工小作坊许可申请书
- 2025年度黑龙江人才周佳木斯市急需紧缺专业技术人才引进227人笔试参考试题(中国农业大学专场)附答案解析
- 医疗设备维护与质量控制
- 2026年安徽邮电职业技术学院单招职业技能测试必刷测试卷附答案
评论
0/150
提交评论