32位汇编语言程序设计 第3版 课件 第5、6章 模块化程序设计、Windows编程_第1页
32位汇编语言程序设计 第3版 课件 第5、6章 模块化程序设计、Windows编程_第2页
32位汇编语言程序设计 第3版 课件 第5、6章 模块化程序设计、Windows编程_第3页
32位汇编语言程序设计 第3版 课件 第5、6章 模块化程序设计、Windows编程_第4页
32位汇编语言程序设计 第3版 课件 第5、6章 模块化程序设计、Windows编程_第5页
已阅读5页,还剩127页未读 继续免费阅读

下载本文档

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

文档简介

第5章模块化程序设计5.1子程序结构5.2参数传递5.3多模块程序结构5.4宏结构第5章模块化程序设计以子程序结构为主体,围绕数码转换实现键盘输入和显示输出掌握子程序、文件包含、宏汇编等各种多模块编程的方法35.1子程序结构经常用到的应用问题编写成一个通用子程序大型处理过程分解成能够解决的模块使用子程序可以使程序的结构更为清楚程序的维护更为方便有利于大程序开发时的多个程序员分工合作子程序(Subroutine)=函数(Function)=过程(Procedure)45.1.1子程序指令子程序与主程序分开、完成特定功能的一段程序主程序(调用程序)执行调用指令CALL调用子程序子程序(被调用程序)执行返回指令RET返回主程序CALLlabel主程序RET子程序回到CALL指令后的指令处51.子程序调用指令CALLCALL指令用在主程序中,实现子程序的调用分成段内调用(近调用)和段间调用(远调用)目标地址采用相对寻址、直接寻址或间接寻址入栈返回地址:将CALL下条指令的地址压入堆栈

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

CALLreg32/reg16

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

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

RET

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

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

;ESP←ESP+i16MASM会根据存储模型等信息确定子程序的远近调用,并相应产生返回指令7name …

RET

CALLnamenext:… ;下条指令CALL的调用和RET的返回主程序子程序堆栈next;思考如下代码片段的功能 callnextnext: popeax

;EAX=?83.过程定义伪指令MASM利用过程定义伪指令获得子程序信息

过程名 PROC

…… ;过程体

过程名 ENDP

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

;代码段,主程序00000000 B800000001

moveax,100000005 BD00000005

movebp,50000000A E800000016

callsubp

;子程序调用0000000F B900000003 retp1: movecx,300000014 BA00000004 retp2: movedx,400000019 E800000000E

calldisprd10〔例5-1〕子程序调用程序-2 ;代码段,子程序subp proc ;过程定义,过程名为subp

pushebp

mov

ebp,esp

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

mov

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

movebx,2

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

ret ;子程序返回subp

endp ;过程结束MOV[EBP+4],EDI?示意图11子程序调用的堆栈返回125.1.2子程序设计子程序的编写方法与主程序一样但需要留意几个问题:利用过程定义,获得子程序名和调用属性RET指令返回主程序,CALL指令调用子程序压入和弹出操作要成对使用,保持堆栈平衡开始保护寄存器,返回前相应恢复安排在代码段的主程序之外子程序允许嵌套和递归最好有完整的注释难点是参数传递13〔例5-2〕回车换行子程序dpcrlf proc ;回车换行子程序

pusheax ;保护寄存器

moval,0dh ;输出回车字符

calldispc ;子程序中调用子程序,实现子程序嵌套

moval,0ah ;输出换行字符

calldispc

popeax ;恢复寄存器

ret ;子程序返回dpcrlf

endp145.2参数传递主程序与子程序间通过参数传递建立联系入口参数(输入参数):主程序→子程序出口参数(输出参数):子程序→主程序传递参数的多少反映程序模块间的耦合程度参数的具体内容数据本身(传递数值)数据的存储地址(传递地址,传递引用)参数传递方法寄存器变量堆栈155.2.1寄存器传递参数最简单和常用的参数传递方法把参数存于约定的寄存器少量数据直接传递数值大量数据只能传递地址带有出口参数的寄存器不能保护和恢复带有入口参数的寄存器可以保护、也可以不保护,但最好能够保持一致16〔例5-3〕十六进制显示程序-1

mov

eax,1234abcdh ;假设一个数据

xor

ebx,ebx

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

pusheax ;mov

edx,eax

callhtoasc ;调用子程序

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

popeax ;mov

eax,edx

incebx

dec

ecx

jnzagain

mov

eax,offset

regd calldispmsg ;显示regddb'EAX=',8dup(0),'H',017〔例5-3〕十六进制显示程序-2

;子程序htoasc proc

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

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

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

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

jbe

htoend

addal,7 ;是A~F,ASCII码再加上7htoend:

ret ;子程序返回htoasc

endp18〔例5-3〕十六进制显示程序-3

;子程序htoasc proc

andeax,0fh ;取AL低4位

mov

al,ASCII[eax] ;换码

ret ;子程序的局部数据(只读)ASCII db'0123456789ABCDEF'htoasc

endpEAX=1234ABCDH运行结果19〔例5-4〕有符号十进制数显示程序-1转换的算法如下:

1)首先判断数据是零、正数或负数,是零显示“0”退出 2)是负数,显示负号“-”,求数据的绝对值 3)接着数据除以10,余数为十进制数码,加30H转换为ASCII码保存 4)重复第(3)步,直到商为0结束 5)依次从高位开始显示各位数字20〔例5-4〕有符号十进制数显示程序-2

;数据段array dd1234567890,-1234,0,1,...count equ($-array)/4writebuf db12dup(0) ;显示缓冲区

;代码段

mov

ecx,count

movebx,0again: mov

eax,array[ebx*4] ;EAX=入口参数

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

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

incebx

dec

ecx

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

pushebx ;保护寄存器

pushecx pushedx

mov

ebx,offset

writebuf ;EBX指向显示缓冲区

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

jnzwrite1 ;不是零,跳转

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

incebx

jmpwrite5 ;转向显示write1: jnswrite2 ;是正数,跳转寄存器传递参数22〔例5-4〕有符号十进制数显示程序-4

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

incebx

neg

eax ;数据求补(绝对值)write2: movecx,10

pushecx ;10压入堆栈,作为退出标志write3: cmpeax,0 ;数据(商)为零,转向保存

jzwrite4

xor

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

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

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

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

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

cmp

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

jewrite5

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

incebx

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

mov

eax,offset

writebuf calldispmsg

popedx ;恢复寄存器

popecx popebx

ret ;子程序返回write endp245.2.2共享变量传递参数子程序和主程序使用同一个变量名存取数据如果变量定义和使用不在同一个程序模块中,需要利用PUBLIC、EXTREN声明共享变量传递参数,子程序的通用性较差特别适合在多个程序段间、尤其在不同的程序模块间传递数据25〔例5-5〕二进制输入程序-1

;数据段count equ5array ddcountdup(0)temp dd0 ;共享变量

;代码段,主程序

mov

ecx,count

mov

ebx,offsetarrayagain: callrdbd

;调用子程序,输入一个数据

mov

eax,[temp]

;获得出口参数

mov[ebx],eax

;存放到数据缓冲区

addebx,4 loopagain共享变量传递参数26〔例5-5〕二进制输入程序-2

;代码段,子程序rdbd proc

;二进制输入子程序

pusheax

;出口参数:共享变量TEMP

pushebx pushecxrdbd1: xor

ebx,ebx

;EBX用于存放二进制结果

movecx,32

;限制输入字符的个数rdbd2: callreadc

;输入一个字符

cmpal,'0'

;检测键入字符是否合法

jb

rderr

;不合法则返回重新输入

cmpal,'1'

ja

rderr27〔例5-5〕二进制输入程序-3

subal,'0'

;对输入的字符进行转化

shlebx,1

;EBX的值乘以2

orbl,al

;BL和AL相加

looprdbd2

;循环键入字符

mov

[temp],ebx

;把二进制结果存放TEMP返回

calldispcrlf

;分行

popecx popebx popeax

ret共享变量传递参数28〔例5-5〕二进制输入程序-4rderr: mov

eax,offset

errmsg ;显示错误信息

calldispmsg

jmprdbd1errmsg db0dh,0ah,'Inputerror,enteragain:',0rdbd

endp29〔例5-6〕有符号十进制数输入程序-1十进制有符号整数转换为补码的算法如下: 1)判断输入了正数、还是负数,用一个寄存器记录 2)判断下一个字符是否为有效数码 字符无效,提示错误重新输入,并转向(1) 字符有效,继续 3)字符有效,减30H转换为二进制数;然后将前面输入的数值乘10,并与刚输入的数字相加得到新的数值 4)判断输入的数据是否超出有效范围超出范围,提示错误重新输入,并转向(1)没有超出范围,则继续 5)重复第(2)~(4)步,输入字符都有效,一直处理完 6)是负数进行求补,转换成补码;否则直接将数值保存30〔例5-6〕有符号十进制数输入程序-2

;数据段count equ10array ddcountdup(0)temp dd0readbuf db30dup(0) ;代码段

mov

ecx,count

mov

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

mov

eax,[temp] ;获得出口参数

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

addebx,4

dec

ecx

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

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

pushebx

pushecx pushedxread0: mov

eax,offset

readbuf

callreadmsg ;输入一个字符串

testeax,eax

jz

readerr ;没有输入数据,错误

cmpeax,12

ja

readerr ;输入超过12个字符,错误32〔例5-6〕有符号十进制数输入程序-4

mov

edx,offset

readbuf;EDX指向输入缓冲区

xor

ebx,ebx ;EBX保存结果

xor

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

mov

al,[edx] ;取一个字符

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

jzread1

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

jnzread2

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

mov

al,[edx]

testal,al ;是结尾0,转向求补码

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

jb

readerr

cmpal,'9'

ja

readerr subal,30h ;是0~9之间的数码,转换

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

jc

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

movzx

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

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

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

jberead1 ;继续转换下一个数位34〔例5-6〕有符号十进制数输入程序-6readerr: mov

eax,offset

errmsg ;显示出错信息

calldispmsg

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

jzread4

neg

ebx ;是负数,进行求补

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

ja

readerr35〔例5-6〕有符号十进制数输入程序-7read5: mov

[temp],ebx ;设置出口参数

popedx popecx popebx popeax

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

;数据段array dd675,354,-34,...count equ($-array)/4 ;代码段

pushdwordptrcount ;压入数据个数

pushoffsetarray ;压数组的偏移地址

callmean ;调用求平均值子程序

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

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

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

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

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

mov

ebp,esp

pushebx ;保护寄存器

pushecx pushedx

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

movecx,[ebp+12]

;ECX=取出的数据个数

xor

eax,eax ;EAX保存和值

xor

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

addedx,1 ;指向下一个数据

cmp

edx,ecx ;比较个数

jbmean1 ;循环

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

idiv

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

popedx ;恢复寄存器

popecx popebx

popebp

retmean endp求和溢出与个数为0的问题?示意图40利用堆栈传递参数返回415.3多模块程序结构程序分段、子程序等实现了程序模块化开发大型应用程序时常使用多个源程序文件、目标代码模块等组成完整的程序MASM具体支持的方法源文件包含模块连接子程序库库文件包含425.3.1源文件包含大型源程序可以合理地分放在若干个文本文件中各种常量定义、声明语句等组织在包含文件(*.INC)常用的或有价值的宏定义存放在宏定义文件(*.MAC)常用的子程序形成汇编语言源文件(*.ASM)任何文本文件使用源文件包含伪指令INCLUDE将指定的文本文件内容插入主体源程序文件

INCLUDE文件名利用INCLUDE伪指令包含其他文件,其实质仍然是一个源程序,只不过是分在了几个文件书写子程序库文件名要符合操作系统规范43〔例5-8〕存储器数据显示程序-1;文件名:eg0508.inc,例5-8程序的数据段内容

.data ;数据段dvar

dd1234abcdhinmsg db'EnterMemoryAddress:',0outmsg1 db'MemoryDataInHexDecimal:',0outmsg1 db'MemoryDataInSignedDecimal:',0temp dd0 ;共享变量writebuf db12dup(0) ;十进制输出的显示缓冲区包含文件44〔例5-8〕存储器数据显示程序-2;文件名:eg0508.asm,例5-8主程序

includeio32.inc ;源文件包含:32位I/O文件

includeeg0508.inc ;源文件包含:数据段文件

.code ;代码段,主程序start: mov

dwordptr[temp],offset

dvar calldphd ;十六进制输出,显示变量DVAR地址以便输入

calldispcrlf

mov

eax,offset

inmsg calldispmsg

callrdhd ;输入存储器地址,EAX返回

calldispcrlf主程序文件45〔例5-8〕存储器数据显示程序-3

mov

ebx,[eax] ;EBX=存储器数据

mov

eax,offsetoutmsg1 calldispmsg

mov[temp],ebx ;共享变量传递参数

calldphd ;十六进制输出

calldispcrlf

mov

eax,offsetoutmsg2 calldispmsg

mov

eax,ebx ;寄存器传递参数

callwrite ;十进制输出

exit0 ;主程序结束,退出

includeeg0508s.asm ;源文件包含:子程序文件

endstart主程序文件46〔例5-8〕存储器数据显示程序-4;文件名:eg0508s.asm,例5-8程序的子程序rdhd proc ;十六进制输入子程序

pushebx ;出口参数:EAX=输入的数据

pushecxrdhd1: xor

ebx,ebx ;EBX用于存放十六进制结果

movecx,8 ;限制输入字符的个数rdhd2: callreadc ;输入一个字符

cmpal,'0' ;检测键入字符是否合法

jb

rderr ;不合法则返回重新输入

cmpal,'9'

jberdhd4 ;输入数码:0~9,减30H子程序文件47〔例5-8〕存储器数据显示程序-5

cmp

al,'A'

jb

rderr

cmp

al,'F'

jberdhd3 ;输入大写字母:A~F,减7后、再减30H

cmp

al,'a'

jb

rderr

cmp

al,'f' ;输入小写字母:a~f,减20H、减7后、再减30H

ja

rderr subal,20h ;减20Hrdhd3: subal,7 ;减7rdhd4: subal,30h ;减30H 子程序文件48〔例5-8〕存储器数据显示程序-6

shlebx,4 ;EBX左移4位对应十六进制一位

orbl,al ;BL和AL相加

looprdhd2 ;循环键入字符

mov

eax,ebx ;通过EAX返回结果

popecx popebx retrderr: mov

eax,offset

errmsg calldispmsg

jmprdhd1errmsg db0dh,0ah,'Inputerror,enteragain:',0rdhd

endp子程序文件示意图49例5-8程序的运行示例返回505.3.2模块连接子程序单独编写,汇编形成目标模块OBJ文件连接时输入子程序模块文件名用共用伪指令PUBLIC和外部伪指令EXTERN声明PUBLIC标识符[,标识符…]

;定义标识符的模块使用EXTERN标识符:类型[,标识符:类型…]

;调用标识符的模块使用子程序在代码段,与主程序文件采用相同的存储模型,没有开始执行和结束执行点处理好子程序与主程序之间的参数传问题51〔例5-8〕存储器数据显示程序-7;文件名:eg0508es.asm,例5-8程序的子程序

includeio32.inc

publicrdhd,dphd,write ;子程序共用

externtemp:dword ;外部变量

.data ;数据段writebuf db12dup(0) ;显示缓冲区

.code ;代码段,子程序rdhd proc

c ;十六进制输入子程序 … ;略,同EG0508S.ASM文件

end ;汇编结束子程序文件52〔例5-8〕存储器数据显示程序-8;文件名:eg0508e.asm,例5-8主程序 includeio32.inc

…… ;数据段

externrdhd:near,dphd:near,write:near

;外部子程序

publictemp ;变量共用

.code ;代码段

……主程序文件ML/c/coffeg0508e.asmML/c/coffeg0508es.asmLINK32/subsystem:consoleeg0508e.objeg0508es.obj/libpath:bin操作过程535.3.3子程序库子程序库:统一管理子程序模块遵循更加严格的子程序模块要求子程序文件编写完成、汇编形成目标模块利用库管理工具程序LIB.EXE:把子程序模块逐个加入到子程序库(.LIB) 使用子程序库:在连接主程序模块时提供子程序库文件名LIB32/OUT:eg0508.libeg0508es.objLINK32/subsystem:consoleeg0508e.objeg0508.lib/libpath:bin操作过程54库文件包含直接在源文件中用库文件包含伪指令INCLUDELIB说明不用在连接时输入库文件名

INCLUDELIB文件名;declareprocedures

externreadc:near,readmsg:near externdispc:near,dispmsg:near,dispcrlf:near ...;declareI/Olibraries

includelibio32.libIO32.INC555.4宏结构本书统称的宏结构宏汇编重复汇编条件汇编用于简化源程序结构子程序等多模块结构用于简化目标代码565.4.1宏汇编宏(Macro):具有宏名的一段汇编语句序列宏需要先定义宏名 MACRO形参表

…… ;宏定义体

ENDM然后程序中进行宏调用宏名实体参数在汇编时,宏指令被汇编程序用宏定义的代码序列替代,实现宏展开571.宏定义和宏调用宏定义WriteString macromsg

pusheax leaeax,msg calldispmsg popeax

endm宏展开

pusheax leaeax,msg calldispmsg popeax宏调用

WriteString

msg58OF1131

12SF7ZF6AF40PF2CF0〔例5-9〕状态标志显示程序-1 ;宏定义rfbit macrobit1,bit2

xor

ebx,ebx ;EBX清0,用于保存字符

roleax,bit1 ;将某个标志左移BIT1位,进入当前CF

adcebx,30h ;转换为ASCII字符

mov[rfmsg+bit2],bl ;保存于字符串BIT2位置

endm ;数据段rfmsg db'OF=0,SF=0,ZF=0,AF=0,PF=0,CF=0',13,10,059OF1131

12SF7ZF6AF40PF2CF0〔例5-9〕状态标志显示程序-2 ;代码段

moveax,50 subeax,80 ;假设一个运算

pushfd ;将标志位寄存器的内容压入堆栈

popeax ;将标志位寄存器的内容存入EAX

rfbit21,3 ;显示OF(原来的OF需左移21位,进入当前CF)

rfbit4,9 ;显示SF(原来的SF再左移4位,进入当前CF) ……60OF1131

12SF7ZF6AF40PF2CF0〔例5-9〕状态标志显示程序-3

rfbit21,3 ;显示OF(原来的OF需左移21位,进入当前CF)1 xor

ebx,ebx ;EBX清0,用于保存字符1 roleax,21 ;将某个标志左移BIT1位,进入当前CF1 adcebx,30h ;转换为ASCII字符1 mov[rfmsg+3],bl ;保存于字符串BIT2位置612.宏的参数和宏的操作符(1)宏定义asciiz macrostring db'&string&',0

endm宏展开

db’Thisisaexample.’,0宏调用

asciiz<Thisisaexample.>传递操作符替换操作符622.宏的参数和宏的操作符(2)宏定义asciiz macrostring db'&string&',0

endm宏展开

db’0<Number<10’,0宏调用

asciiz<0!<Number!<10>转义操作符632.宏的参数和宏的操作符(3)宏定义asciiz macrostring db'&string&',0

endm宏展开

db’1023’,0宏调用

asciiz%(1024-1)表达式操作符643.宏的伪指令局部伪指令

LOCAL标号列表

;宏定义体采用了标号,应使用LOCAL加以说明

;它必须是宏定义MACRO语句之后的第一条语句删除宏定义伪指令

PURGE宏名表

;不需要某个宏定义时,可以把它删除宏定义退出伪指令

EXITM

;表示结束当前宏调用的展开65局部伪指令宏定义absol macrooprd

localnext

cmpoprd,0

jgenext

neg

oprdnext:

endm

;;这个伪指令要独占一行宏调用和宏展开

absolwordptr[ebx]

cmpwordptr[ebx],0

jge

??0000

negwordptr[ebx]??0000:66〔例5-10〕通用寄存器显示程序-1dreg32 macroreg32

localdreg1,dreg2

moveax,reg32 ;;显示reg32寄存器

movecx,8

xor

ebx,ebxdreg1:

roleax,4

mov

edx,eax anddl,0fh adddl,30h

;;转化为相应的ASCII码值

cmpdl,39h

;;区别0~9和A~F数码

jbe

dreg2 adddl,767〔例5-10〕通用寄存器显示程序-2dreg2:

movrd®32&[ebx+4],dl ;;存于对应的字符串

incebx

cmp

ebx,ecx

jb

dreg1

endm ;数据段rdeax db'EAX=00000000,'

……

;代码段,显示EAX dreg32eax

……

684.宏与子程序仅是源程序级的简化:宏调用在汇编时进行程序语句的展开,不需要返回;不减小目标程序,执行速度没有改变通过形参、实参结合实现参数传递,简捷直观、灵活多变当程序段较短或要求较快执行时,应选用宏还是目标程序级的简化:子程序调用在执行时由CALL指令转向、RET指令返回;形成的目标代码较短,执行速度减慢需要利用寄存器、存储单元或堆栈等传递参数当程序段较长或为减小目标代码时,要选用子程序695.4.2重复汇编重复汇编指在汇编过程中,重复展开一段相同或基本相同的语句重复汇编没有名字,不能被调用重复汇编常用在宏定义体中,也可以在一般汇编语句中使用重复汇编伪指令有3个:REPEAT ;按参数值重复FOR ;按参数个数重复FORC ;按参数的字符个数重复最后,用ENDM结束701.按参数值重复伪指令REPEATREPEAT重复次数

……

;重复体ENDMchar=20hdbcharrepeat95-1char=char+1dbcharendmdbchar ;等效于db20hchar=char+1dbchar ;等效于db21h ...char=char+1dbchar ;等效于db7eh712.按参数个数重复伪指令FORFOR形参,〈实参表〉

……

;重复体ENDMforregad,<eax,ebx,ecx,edx>

pushregadendmpusheaxpushebxpushecxpushedx723.按参数字符个数重复伪指令FORCFORC

形参,字符串

……

;重复体ENDMforcregad,dcbapope®ad&xendmpopedxpopecxpopebxpopeax735.4.3条件汇编根据某种条件确定是否汇编某段语句序列它与高级语言的条件编译命令类似条件汇编伪指令的一般格式:

IFxx

表达式

;条件满足,汇编分支语句体1

分支语句体1 [ELSE ;条件不满足,汇编分支语句体2

分支语句体2]

ENDIF ;条件汇编结束741.IF/IFE伪指令pdata macronum

ifnumlt100 ;如果num<100,汇编如下语句dbnumdup(?)

else ;否则,汇编如下语句db100dup(?)

endif

endmpdata12 ;宏调用①db12dup(?) ;宏汇编结果①----------------------------pdata102 ;宏调用②db100dup(?) ;宏汇编结果②752.IFDEF/IFNDEF伪指令ifdef

RealMode ;当定义有RealMode符号时,汇编如下语句

mov

ax,@data

mov

ds,axendifRealMode=1 ;定义RealMode符号763.IFB/IFNB伪指令maxnum macrodvar1:req,dvar2,dvar3 localmaxnum1,maxnum2

moveax,dvar1

ifnb<dvar2>

;当有DVAR2实参时,汇编如下语句

cmpeax,dvar2

jgemaxnum1

moveax,dvar2

endifmaxnum1: ……

endm

maxnum3,5 ;宏调用

moveax,3

cmpeax,5

jge??0002

moveax,5??0002:774.IFIDN/IFDIF和IFIDNI/IFDIFI伪指令print macroformat,var

mov

al,var

ifidni<format>,<b> calldispbb

;二进制显示用格式符“b”

exitm

;不再进行宏展开

endif

……

endmprint

b,var;二进制形式显示:01100100第5章习题:模块化程序设计5.1简答题(1、4、5、6、7)5.2

判断题(2、5、6、7、8)5.3

填空题(1、4、5、6、7)5.7、5.8、5.11、5.13第6章Windows编程6.1操作系统函数调用6.2控制台应用程序6.3图形窗口应用程序第6章Windows编程熟悉汇编语言调用API函数的方法掌握控制台输入输出函数熟悉MASM的高级特性理解Windows图形窗口程序的编写816.1

操作系统函数调用操作系统以其提供的系统函数(系统功能Systemfunction)支持程序员进行编程Windows的系统函数(功能)以动态连接库DLL(Dynamic-LinkLibrary)形式提供,利用其应用程序接口API(ApplicationProgramInterface)调用DDL库中的函数API是一些类型、常量和函数的集合,提供了编程中使用库函数的途径Win16:16位Windows的APIWin32:32位Windows的API826.1.1

动态连接库静态连接:连接程序从库文件中抽取需要的子程序插入到最终的可执行代码中动态连接:程序运行时才将代码加载到主存动态连接库:保存程序运行时需要重复使用的代码的文件3个最重要的Windows动态连接库KERNEL32.DLL:主要处理内存管理和进程调度USER32.DLL:主要控制用户界面GDI32.DLL:负责图形方面的操作导入库(ImportLibrary):程序开发的连接阶段使用,与一个动态连接库DLL对应836.1.2MASM的过程声明和调用过程声明伪指令PROTO: 事先声明过程的结构 (包括操作系统API函数、高级语言的函数)

过程名 PROTO [调用距离][语言类型]

[,[参数]∶类型]...过程调用伪指令INVOKE

实现过程调用

INVOKE 过程名[,参数,...]846.1.3

程序退出函数Win32程序员参考手册VOIDExitProcess(UINTuExitCode //exitcodeforallthreads);汇编语言声明ExitProcess

PROTO,:DWORD汇编语言调用invoke

ExitProcess,0将函数调用定义成宏exit MACROdwexitcode

invokeExitProcess,dwexitcode

ENDM宏调用exit 0856.2

控制台应用程序Windows应用程序开始运行创建控制台(Console)窗口或创建图形界面窗口32位Windows控制台程序像增强版的MS-DOS程序使用标准控制台

标准输入设备(键盘)

标准输出设备(显示器)32位控制台程序运行在保护方式通过API使用Windows的动态链接库函数866.2.1控制台输出编写控制台程序需要调用控制台函数,实现控制台(显示器)输出控制台(键盘)输入几乎所有的控制台函数都要求将控制台句柄作为第一个参数传递给它们本节介绍的控制台函数存在于KERNEL32.DLL动态库中程序开发需要使用KERNEL32.LIB导入库文件871.

控制台句柄句柄(Handle)是一个32位无符号整数用来唯一确定一个对象例如某个输入设备、输出设备或者一个图形标准输入句柄

STD_INPUT_HANDLE

equ-10标准输出句柄

STD_OUTPUT_HANDLE

equ-11标准错误句柄

STD_ERROR_HANDLE

equ-12GetStdHandle函数获取控制台输入或输出的句柄实例用于控制台程序的输入输出操作882.

控制台输出函数显示器输出API函数WriteConsole将一个字符串输出到屏幕上支持标准的ASCII控制字符,例如回车、换行等Win32API中可以使用两种字符集8位ASCII字符集,函数名以字母A结尾16位Unicode字符集,函数名以字母W结尾WriteConsole参数第一个:控制台输出句柄实例第二个:指向字符串的指针、即缓冲区地址第三个:指明字符串长度,是一个32位整数第四个:指向一个整数变量,返回实际输出的字符数第五个:保留,设置为089〔例6-1〕控制台输出程序-1

.686 .modelflat,stdcall optioncasemap:none

includelib

bin\kernel32.libExitProcess

proto,:dwordGetStdHandle proto,:dwordWriteConsoleA proto,:dword,:dword,:dword,:dword,:dwordWriteConsole equ<WriteConsoleA>STD_OUTPUT_HANDLEequ-11 .datamsg db'Hello,Assembly!',13,10count equ$-msgoutsize dd090〔例6-1〕控制台输出程序-2 .codestart:

;获得输出句柄 invokeGetStdHandle,STD_OUTPUT_HANDLE

;显示信息 invokeWriteConsole,eax,offsetmsg,count,offsetoutsize,0

;退出 invokeExitProcess,0 endstartHello,Assembly!运行结果916.2.2

控制台输入键盘输入API函数ReadConsole将键盘输入的文本保存到一个缓冲区第一个:控制台输入句柄实例第二个:输入缓冲区指针第三个:要读取字符的最大数量第四个:实际输入字符数量的指针第五个:未使用,设置为0调用ReadConsole函数时系统等待用户输入、并回车确认(例如用户输入了3个字符,依次是123)第4个参数的变量保存用户输入字符个数再加2的结果(例如本例是5)(内容用十六进制数表达依次是3132330D0A)定义输入缓冲区要多留两个字节92〔例6-2〕信息输入输出程序-1

.686 .modelflat,stdcall optioncasemap:none

includelib

bin\kernel32.libExitProcess

proto,:dwordexit MACROdwexitcode invokeExitProcess,dwexitcode ENDMGetStdHandle

proto,:dwordWriteConsoleA

proto,:dword,:dword,:dword,:dword,:dwordWriteConsole

equ<WriteConsoleA>93〔例6-2〕信息输入输出程序-2ReadConsoleA proto,:dword,:dword,:dword,:dword,:dwordReadConsole equ<ReadConsoleA>STD_INPUT_HANDLEequ-10STD_OUTPUT_HANDLEequ-11 .datamsg1 db'Pleaseenteryourname:',0msg2 db'Welcome',0nbuf db80dup(0)msg3 db'toWin32Console!',0

;设置输入缓冲区最大255个字符94〔例6-2〕信息输入输出程序-3

.codestart:

mov

eax,offsetmsg1 ;提示输入

calldispmsg

mov

eax,offset

nbuf

;输入信息

callreadmsg

mov

eax,offsetmsg2

calldispmsg

mov

eax,offset

nbuf

;显示输入信息

calldispmsg

mov

eax,offsetmsg3

calldispmsg

exit095〔例6-2〕信息输入输出程序-4 .data_outsize dd0_outhandle dd0dispmsg proc

;字符串显示子程序

pusheax

;入口参数:EAX=字符串地址

pushebx pushecx pushedx pusheax

;保存入口参数,即字符串地址

invokeGetStdHandle,STD_OUTPUT_HANDLE

mov[_outhandle],eax

popebx

;EBX=字符串地址

xor

ecx,ecx

;计算字符串长度96〔例6-2〕信息输入输出程序-5dispm1: moval,[ebx+ecx] testal,al jzdispm2 incecx jmpdispm1dispm2:

invokeWriteConsole,[_outhandle],ebx,ecx,offset_outsize,0

popedx popecx popebx popeax retdispmsg

endp97〔例6-2〕信息输入输出程序-6 .data_insize dd0_inbuffer db255dup(0)readmsg proc ;字符串输入子程序

pushebx ;入口参数:EAX=缓冲区地址

pushecx pushedx pusheax

;保护输入的缓冲区地址参数

invokeGetStdHandle,STD_INPUT_HANDLE invokeReadConsole,eax,offset_inbuffer,255,offset_insize,0

subdwordptr[_insize],2

xor

ecx,ecx popebx

;获得缓冲区地址98〔例6-2〕信息输入输出程序-7readm1: moval,_inbuffer[ecx] mov[ebx+ecx],al ;将输入的字符串复制到用户缓冲区

incecx cmpecx,[_insize] jbreadm1

movbyteptr[ebx+ecx],0

;最后填入结尾字符0

mov

eax,ecx popedx popecx popebx retreadmsg

endpPleaseenteryourname:JerryWelcomeJerrytoWin32Console!运行结果996.2.3单字符输入默认模式下ReadConsole函数实现一行字符输入最后必须用回车键结束设置为单字符输入模式后ReadConsole函数实现一个字符输入自动结束调用需要修改输入模式获取控制台模式函数GetConsoleMode设置控制台模式函数SetConsoleMode100〔例6-3〕单字符输入程序-1 …GetConsoleModeproto,:dword,:dwordSetConsoleModeproto,:dword,:dword .datamsg db'Pressanykeytoend'count equ$-msgoutsize dd0inhandle dd0savemode dd0 ;保存控制台模式insize dd0inbuffer db255dup(0) ;设置输入缓冲区最大255个字符101〔例6-3〕单字符输入程序-2

.codestart: invokeGetStdHandle,STD_OUTPUT_HANDLE

;提示按任意键

invokeWriteConsole,eax,offsetmsg,count,offsetoutsize,0 invokeGetStdHandle,STD_INPUT_HANDLE mov[inhandle],eax

invokeGetConsoleMode,[inhandle],offsetsavemode

;获得控制台模式

invokeSetConsoleMode,[inhandle],0

;设置为单字符输入模式102〔例6-3〕单字符输入程序-3

invokeReadConsole,[inhandle],offsetinbuffer,1,offsetinsize,0

;输入字符

invokeSetConsoleMode,[inhandle],[savemode]

;恢复原控制台模式

invokeExitProcess,0 ;退出Pressanykeytoend运行结果1036.3

图形窗口应用程序Windows图形界面以窗口、对话框、菜单、按钮等实现用户交互用汇编语言编写图形窗口应用程序就是调用这些API函数1046.3.1

消息窗口消息窗口是常见的Windows图形窗口显示形式使用MessageBox函数Win32程序员参考手册的定义int

MessageBox(HWNDhWnd,LPCTSTRlpText,LPCTSTRlpCaption,UINTuType);hWnd:父窗口的句柄lpText:要显示字符串的地址指针,字符串的首地址lpCaption:消息窗标题的地址指针uType:指明该消息窗的类型105〔例6-4〕消息窗口程序-1

.686 .modelflat,stdcall optioncasemap:none

includelib

bin\kernel32.lib

includelib

bin\user32.libExitProcess

proto,:dwordMessageBoxA

PROTO,:dword,:dword,:dword,:dwordMessageBox

equ<MessageBoxA>NULL equ0MB_OK equ0 .dataszCaption db‘欢迎',0outbuffer db‘你好,汇编语言!',0106〔例6-4〕消息窗口程序-2 .codestart:

invoke

MessageBox,NULL,offsetoutbuffer,offset

szCaption,MB_OK invokeExitProcess,NULL endstart连接时应该使用参数/subsystem:windows1076.3.2结构变量结构类型的说明 结构名STRUCT …… ;数据定义语句 结构名ENDS结构变量的定义 变量名结构名<字段初值表>结构变量及其字段的引用引用结构变量,只要直接书写结构变量名引用其中某个字段,采用圆点“.”操作符结构变量名.结构字段名108〔例6-5〕系统时钟显示程序-1

;系统时间的结构类型说明SYSTEMTIME

struct

wYear dw0 ;年(4位数)

wMonth dw0 ;月(1~12)wDayOfWeek dw0 ;星期(0~6,0=星期日,……)wDay dw0 ;日(1~31)

wHour dw0 ;时(0~23)

wMinute dw0 ;分(0~59)

wSecond dw0

;秒(0~59)

wMillisconds dw0 ;毫秒(0~999)SYSTEMTIME ends

;函数声明,参数是指向结构变量的指针GetLocalTime proto,:dword109〔例6-5〕系统时钟显示程序-2 .datamytime SYSTEMTIME<> ;系统时间的结构变量定义timestring db'--:--:--',0timecaption db'当前时间',0 .codestart: invokeGetLocalTime,offsetmytime ;获得当前时间

movebx,offsettimestring ;EBX指向“时”的保存位置

writedec

[mytime.wHour]

;转换为ASCII字符

invokeMessageBox,0,offsettimestring,offsettimecaption,1writedec110宏:writedecwritedec macrotime ;;将二进制数转换为2位十进制数

;;再转为ASCII字符保存

movax,time movcl,10 divcl ;;商AL是百位,余数AH是个位

addax,3030h ;;转换为ASCII

mov[ebx],ax ;;对应显示顺序,百位先显示保存在低地址

endm返回1116.3.3MASM的高级语言特性MASM6.0引入高级语言的程序设计特性扩展的过程定义PROC过程声明和过程调用伪指令PROTOINVOKE条件控制伪指令.IF.ELSE.ENDIF循环控制伪指令.WHILE.ENDW.REPEAT.UNTIL要调用带参数过程定义的过程,不应采用CALL指令,因为比较烦琐应该采用过程调用伪指令INVOKE使用INVOKE伪指令的前提是需要用PROTO伪指令对过程进行声明过程名 PROC

调用距离语言类型作用范围<起始参数> USES寄存器列表,参数:类型

LOCAL参数表

… ;汇编语言语句过程名 ENDP过程声明伪指令:事先声明过程的结构过程名 PROTO调用距离语言类型,参数:类型过程调用伪指令

INVOKE

过程名,参数,...如何调用1.扩展的过程定义113〔例6-6〕使用扩展过程定义程序-1mean protoc,:dword,:dword

;过程声明,使用C语言规范

.dataarray dd675,…,-67,4257count equ($-array)/4 .codestart: ;主程序:调用求平均值子程序,然后显示

invokemean,offsetarray,count

calldispsid

;显示 exit0 ;子程序:计算32位有符号数平均值 ;入口参数:D表示数组地址、NUM表示元素个数 ;出口参数:EAX=平均值114〔例6-6〕使用扩展过程定义程序-2mean proccusesebxecxedx,d:dword,num:dword

movebx,d ;EBX=数组指针

movecx,num ;ECX=元素个数

xoreax,eax ;EAX保存和值

xoredx,edx ;EDX=指向数组元素mean1: addeax,[ebx+edx*4] ;求和

addedx,1 ;指向下一个数据

cmpedx,ecx ;比较个数

jbmean1 ;循环

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

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

retmean endp用“/Sa”参数汇编,最大化源代码列表115〔例6-6〕列表文件-1start:

;主程序:调用求平均值子程序,然后显示

invokemean,offsetarray,count* push+00000000Ah* pushOFFSETarray* callmean* addesp,000000008h

calldispsid

;显示

exit0* push+000000000h* callExitProcess1 invokeExitProcess,0116〔例6-6〕列表文件-2mean proccusesebxecxedx,d:dword,num:dword* pushebp* movebp,esp* pushebx* pushecx* pushedx …… ;这部分同源程序,省略

ret* popedx* popecx* popebx* leave

;movesp,ebp/popebp* ret00000hmean endpmovebx,dwordptr[ebp+8]movecx,dwordptr[ebp+0ch]1172.条件控制类似高级语言中IF、THEN、ELSE和ENDIF的相应功能在汇编时要展开,自动生成相应的比较和条件转移指令序列,实现程序分支

.IF条件表达式

;条件为真(值为非0)

分支体

;执行分支体[ .ELSEIF ;前面IF[以及前面ELSEIF]条件为假 ;并且当前ELSEIF条件为真

分支体

]

;执行分支体[ .ELSE ;前面IF[以及前面ELSEIF]条件为假

分支体

]

;执行分支体

.ENDIF ;分支结束条件118条件表达式中的操作符操作符功能操作符功能操作符功能==等于&&逻辑与CARRY?CF=1?!=不等于||逻辑或OVERFLOW?OF=1?>大于!逻辑非PARITY?PF=1?>=大于等于SIGN?SF=1?<小于&位测试ZERO?ZF=1?<=小于等于()改变优先级返回〔例6-7〕使用条件控制的程序-1求EAX绝对值程序单分支结构

callreadsid

.

温馨提示

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

评论

0/150

提交评论