




已阅读5页,还剩15页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第10章 应用程序的设计在前面各章节中,我们侧重介绍了汇编语言程序设计中各组成部分的作用,本章的重点是对前面所学知识的综合运用。希望通过各种不同类型的例子,使读者能够掌握用汇编语言编程的基本技巧。10.1 字符串的处理程序字符或字符串是一类重要的非数值计算的处理对象。许多编辑软件都具有字符串查找、替换、大小写的转换、单词的自动识别等功能,网络上的信息搜索也是现在一种常用的功能等,这些功能的实现无疑都要涉及到字符串的处理功能。为了方便对字符串的处理,各种常用的编程环境也都给予了足够的支持。如:C语言编程环境提供了大量处理字符串的标准函数,象strlen、strcmp和strcpy等函数;C+、VC或VB等编程环境提供了字符串类String等。这些函数或类大大方便了程序员的编程。在计算机系统内,为了加快字符串的处理,在其指令系统中设置了多条处理字符串的指令,其详细内容请参阅第5.2.11节中的介绍。下面我们将通过几个例子来学习汇编语言处理字符串的方法。例10.1 编写一个求字符串长度的子程序Strlen,要求字符串的首地址为入口参数,且以ASCII码0为结束符,CX为出口参数,其存放该字符串的长度。 解:.MODELSMALL, C.DATAbuffDBThis is a example., 0.CODEStrlenPROCUSES AX BX, String:PTR BYTEMOVBX, StringXORCX, CXMOVAL, BX.WHILEAL!=0INCCXINCBXMOVAL, BX.ENDWRETStrlenENDP.STARTUPINVOKEStrlen, ADDR buff.EXIT0END例10.2 编写一个把字符串中的所有小写字符转换成大写字符的子程序Strupr,要求字符串的首地址和结束符为其入口参数。 解:.MODELSMALL, C.DATAbuffDB This is a example., 0.CODEStruprPROC USES AX BX, String:PTR BYTE, Tail:BYTEMOVBX, String.REPEATMOVAL, BX.IFAL=a & AL= CX;若字符起点就超过源串的长度MOVBYTE PTR ES:DI, 0;拷贝的字符串为“空”JMPover.ENDIFADDSI, AX;定源串中字符的起点SIMOVCX, numCLD.REPEATLODSBSTOSB.UNTILCXZ AL=0.IF AL!=0;设置目标串的结束符MOVBYTE PTRDI, 0.ENDIFover:RETStrncpyENDP.STARTUPINVOKEStrncpy, ADDR str2, ADDR str1, 3, 5.EXIT0END例10.4 编写一个把字符串中空格和TAB压缩掉的子程序Compress,字符串String是以ASCII码0为结束符。 解:.MODELSMALL, C.DATASPACEEQU20HTABEQU9HBuffDB12 3 4 Ab cdef, 0.CODECompressPROCUSES AX BX SI DS, String:FAR PTR BYTELDSSI, String;SI用于扫描字符串的指针MOVBX, SI;BX用于存放结果的指针.REPEATMOVAL, SIINCSI.IF AL!=SPACE & AL!=TABMOVBX, ALINCBX.ENDIF.UNTIL AL=0RETCompressENDP.STARTUPINVOKECompress, ADDR Buff.EXIT0END从上面四个例子,我们不难看出处理字符串的一般方法,感兴趣的读者可自行编写实现字符串变小写、整体拷贝、逆转和查找等功能的子程序,甚至还可以建立起自己的字符串处理库文件。10.2 数据的分类统计程序数据的分类和统计也是一类非数值计算,数据的分类统计方法在例6.10中已介绍,下面通过一个例子介绍数据的分类存储问题。 例10.5 统计从地址0040H:0000H开始的100个字中,把正数和负数按照它们先后出现的次序分别存储在缓冲区Data1和Data2,并把每类的个数存入相应缓冲区的第一个字单元中。 解:由于在指定地址之后的100个字中,可能存在全是正数或负数的情况,所以,缓冲区Data1和Data2的容量都应是100个字。.MODELSMALL.DATANum = 100Data1DW?, Num dup(?)Data2DW?, Num dup(?).CODE.STARTUPMOVAX, 40HMOVES, AXLEASI, Data1+2;指向存储正数的缓冲区LEADI, Data2+2;指向存储负数的缓冲区XORBX, BX;BX用于扫描存储单元MOVCX, 100;字符个数.REPEATMOVAX, ES:BXADDBX, 2CMPAX, 0.CONTINUE.IFZERO?JLnext1MOV SI, AX ;向正数缓冲区内存储数据ADDSI, 2.CONTINUEnext1:MOVDI, AX;向负数缓冲区内存储数据ADDDI, 2.UNTILCXZSUBSI, OFFSET Data1+2SUBDI, OFFSET Data2+2SHRSI, 1SHRDI, 1MOVData1, SIMOV Data2, DI ;把每类的统计个数存入缓冲区的第一个字单元.EXIT0END 例10.6 用键盘输入任意一字符串,分类统计该字符串中每个数字和字母的出现次数。 解:.MODELSMALL.DATAN = 80BuffDB N, ?, N DUP(?)NumDW 36 DUP(0);每个字用于存放09,AZ出现的个数.CODE.STARTUPLEADX, BuffMOVAH, 0AHINT21H;输入一个字符串XORCH, CHMOVCL, Buff+1;CX=输入字符串的个数LEASI, Buff+2XORBX, BX.REPEATMOVBL, SI;考虑下面的思考题INCSI.IFBL=0 & BL=a & BL=A & BL=Z;分类统计AZ中的每个字母的个数SUBBL, A-10ADDBX, BXINCNumBX.ENDIF.UNTILCXZ.EXIT 0END思考题:在本例中,用指令“MOV BL, SI”来把当前检测的字符存入BL,当然,我们也可以用AL来代替BL,有关指令要作相应的改动,但这样做,会更方便吗?希望读者能知道:为什么要用BL,而不用AL?10.3 数据转换程序数据类型转换是输入输出过程中经常遇到的问题。输入时,计算机系统要把用户从键盘上输入的字符串转变成相应的数值,并存储在内存中;输出时,要把计算机内部的二进制数据形式转换成相应的十进制字符串,然后再输出。 在高级语言编程环境中,程序员能用各种输入输出语句,按一定的格式进行交互式操作,很少或根本不关心输入输出是如何实现的。有的程序员甚至认为其输入的就是十进制数值,输出数据也就是把内存中存储的数据直接输出出来。其实,输入输出过程并不是如此简单,计算机系统要进行复杂而又细致的数据类型转换和格式化等工作。 本节试图通过用汇编语言实现数据类型的转换来反映输入输出的本来面目,使程序员在用高级语言编程时,对其输入输出语句的实现过程有所了解,也知道有别人(或编译程序)帮他完成了输入输出的准备工作。 例10.7 编写一个程序,它能把字类型变量的数值以十进制形式输出出来。若该数值为负数,则需要输出负号-,否则,不输出符号。解:鉴于按二进制输出的特殊性,我们可以把它优化成例10.8的形式,按十六进制输出也可以按“四位二进制对应一位十六进制”的规则进行优化的。 例10.8 编写程序,它能把字类型变量的数值以二、八进制或十六进制形式输出出来。若该数值为负数,则需要输出负号-,否则,不输出符号。 解:一、按二进制形式输出的程序二、按八进制形式输出的程序三、按十六进制形式输出的程序例10.7是用“用16位除10”的方法从低向高依次得到每位的数值,但若待输出的数据是32位,用10除之后,其商很可能会超过16位,所以,不能简单地引用例10.7的方法来输出32位二进制。 假设:32位二进制数Z为A216B,其中:A和B都是16位二进制数。用10去除A,得:AA110A2,于是, (1) 假设A2216B被10除后所得的商和余数分别为B1和C1(B10,C10)。 利用式(1)和“A210”,我们不难看出:Z的个位就是C1和B1216。 令Z1A1216B1,显然,Z1就是Z/10所得到的商。 对于Z1,再利用式(1)得到商Z2和C2。,重复上面的步骤,直到所得商为0为止。 下面的例10.9就是利用上面方法来输出32位二进制数值。 例10.9 编写一个子程序,该子程序能把32位二进制变量的数值以十进制形式输出出来。若该数值为负数,则需要输出负号-,否则,不输出符号。 解:.MODELSMALL, C.DATACR = 13LF = 10Data1DD 908976789.CODE;子程序Display是按十进制输出32位二进制数值SOURCEDisplayPROC USES AX BX CX DX SI DI SOURCE:DWORDLOCALFLAG:BYTE;定义一个字节类型的局部变量FLAGMOVBX, WORD PTR SOURCEMOVCX, WORD PTR SOURCE+2MOVFLAG, 0;FLAG=0正数CMPCX, 0JGEnextINCFLAG;FLAG=1负数NOTBXNOTCXADDBX, 1;能否用指令INC BX?ADCCX, 0;上四条指令把32位数CX-BX变为正数next:XORDI, DI;压入堆栈字符的个数MOVSI,10;用10来除.REPEAT;本循环把32位二进制数转换成十进制XORDX, DX;数的字符串存入堆栈之中MOVAX, CXDIVSIMOVCX, AXMOVAX, BXDIVSIADDDL, 0PUSHDXINCDIMOVBX, AX.UNTILBX=0 & CX=0.IFFLAG=1;判断前面转换的数是否为负数MOVAL, -;若是,把符号-压入堆栈PUSHAXINCDI.ENDIFMOVCX, DI.REPEAT;本循环把堆栈中的字符串显示出来POPDXMOVAH, 2INT21H.UNTILCXZMOVDL, CR;下面六条指令显示回车、换行MOVAH, 2INT21HMOVDL, LFMOVAH, 2INT21HRETDisplayENDP.STARTUPINVOKEDisplay, Data1INVOKEDisplay, -123456789.EXIT0END例10.10 编写一个程序,它能把用键盘输入的字符串转化成相应的数值。具体功能如下:1、输入的数据字符串可以带正、负符号,如:1234、+1234或-1234;2、字符串的最后一个字符表示数据的进制,默认的进制为十进制,如:1234H表示十六进制数1234,1234为十进制数;3、对于任何进制的数据,当遇到一个非进制范围内的字符时,则显示出错信息,并以数值为其转换结果来结束该类型转换过程。解:略10.4 文件操作程序有关目录和顺序文件的操作在第8.3.6节中已有介绍和举例,本节主要介绍对记录文件的读写方法。记录文件是指文件中的每个分量是一个结构的文件,如:Fox系列数据库管理系统中的DBF文件,该文件除了文件头是由记录文件的整体信息和各字段描述信息之外,文件的主体内容就是由同一个结构组成的。 下面通过二个例子来介绍记录文件的读写方法。 例10.11 假设有一个简单的学生结构类型student,其包括:学号、姓名和年龄等信息,要求编写一个程序,该程序接受从键盘输入的学生记录信息,并把它们保存在文件students.dat之中。 解:.MODEL SMALL, C.486studentSTRUCTidDW ?snameDB 10 DUP(?)ageDB ?studentENDS.DATAfnameDBStudents.dat,0msg1DBId:$msg2DBName:$msg3DBAge:$msg4DBContinue?$msg5DBFail to create file$CRLFDB0AH, 0DH, $buffDB?, ?, 11 DUP(?)peasonSTUDENT .CODEDispMsgPROC USES AX DX, Msg:PTR BYTE;显示字符串Msg;参见例10.7DispMsgENDP;程序功能:把字符串Data转化成数值,不考虑负数。当遇到非法字符时,则结束转换操作;;入口参数:Data为字符串的首地址,Len为该字符串的长度;;出口参数:数值存放在AX中。GetDataPROC USES BX CX SI, Len:BYTE, Data:PTR BYPEXORCX, CXMOVCL, LenMOVSI, DataXORAX, AXXORBX, BX.REPEATMOVBL, SISUBBL, 0.BREAK .IF BL9;判断当前数值是否在09之间IMULAX, 10ADDAX, BXINCSI.UNTILCXZRETGetDataENDP;程序功能:读取指定长度的字符串,在输入前,显示有关输入内容的提示信息;;入口参数:读入字符串的长度为Len,提示信息的首地址为MSG;;出口参数:读入信息(字符串)存放缓冲区buff中。GetInfoPROC USES AX DX, Len:BYTE, Msg:PTR BYTEINVOKEDispMsg, Msg;显示提示信息MOVAL, LenMOVbuff, ALMOVAH, 0AHLEADX, buffINT21HINVOKEDispMsg, ADDR CRLF;显示回车、换行RETGetInfoENDP.STARTUPMOVAX, DSMOVES, AXLEADX, fnameMOVCX, 20HMOVAH, 3CHINT21H;创建文件.IF CARRY?;若创建失败,则显示失败信息INVOKEDispMsg, ADDR msg5JMPover.ENDIFMOVBX, AX;把句柄存入BX,为后面使用作准备again:INVOKEDispMsg, ADDR CRLF;显示回车、换行INVOKEGetInfo, 5, ADDR msg1;读取学号(假定学号为4位整数)INVOKEGetData, 4, ADDR buff+2;把学号字符串转化成数值MOVpeason.id, AX;把数值型学号存入学号字段INVOKEGetInfo, 11, ADDR msg2;读取姓名(假定姓名为10个字符)MOVCX, 10MOVAL, LEADI, peason.snameREPSTOSB;先置姓名字段为10个空格MOVCL, buff+1MOVSI, OFFSET buff+2LEADI, peason.snameREPMOVSB;把输入的姓名存入姓名字段INVOKEGetInfo, 3, ADDR msg3;读取年龄(假定年龄为2位整数)INVOKEGetData, 2, ADDR buff+2;把年龄字符串转化成数值MOVpeason.age, AL;把数值型年龄存入年龄字段MOVCX, SIZE peasonLEADX, peasonMOVAH, 40HINT21H;把学生记录写入文件INVOKEDispMsg, ADDR msg4;提示是否继续输入MOVAH, 1INT21HANDAL, 0DFHCMPAL, YJZagain;若按y或Y,则继续输入MOVAH, 3EHINT21Hover:.EXIT0END例10.12 编写一个程序显示由例10.11建立的记录文件students.dat中的学生信息。 解:.MODELSMALL,CstudentSTRUCTidDW?snameDB10 DUP(?)ageDB?studentENDS.DATAfnameDBStudents.dat,0id1DBId:, 4 dup(?), 0dh, 0ah, $name1DBName:, 10 dup( ), 0dh, 0ah, $age1DBAge:, 2 dup(?), 0dh, 0ah, $msg1DBFail to open file$peasonstudent.CODEDispMsgPROC USES AX DX, Msg:PTR BYTE;显示字符串Msg;参见例10.7DispMsgENDP;程序功能:把数据Data转换成长度为Len的字符串;;入口参数:待转换数据Data,转换成字符串的长度为Len,存放字符串的首地址为PStr;;出口参数:读入信息(字符串)存放缓冲区buff中。GetStrPROC USES AX CX DX DI, Data:WORD, Len:WORD, PStr:PTR BYTEMOVCX, LenMOVDI, PStrMOVAL, REPSTOSB;把存放字符串的缓冲区填充为空格MOVDI, PStrADDDI, LenDECDI;确定最后一个字符在缓冲区中的位置MOVAX, DataMOVCX, 10.REPEATXORDX, DXIDIVCX;除10,从低位向高位求得每一位ADDDL, 0;把余数转变成字符,然后存放目标单元MOVDI, DLDECDI.UNTILAX=0RETGetStrENDP.STARTUPMOVAX, DSMOVES, AXLEADX, fnameMOVAL, 0HMOVAH, 3DHINT21H;以“只读”方式打开指定的文件.IFCARRY?;若创建失败,则显示失败信息INVOKEDispMsg, ADDR msg1JMPover.ENDIFMOVBX, AX;把句柄存入BX,为后面使用作准备again:MOVCX, SIZE peasonLEADX, peasonMOVAH, 3FHINT21H;从文件中读出一个记录.IFCARRY? | AX=0;若读记录出错或遇到文件尾,结束JMPclose.ENDIF INVOKEGetStr, peason.id, 4, ADDR Id1+3;把“学号”转换成字符串INVOKEDispMsg, ADDR Id1;显示“学号”字符串MOVCX, 10LEASI, peason.snameLEADI, Name1+5REPMOVSB;把“姓名”转移到显示区INVOKEDispMsg, ADDR Name1;显示“姓名”字符串INVOKEGetstr, peason.age, 2, ADDR Age1+4;把“年龄”转换成字符串INVOKEDispmsg, ADDR Age1;显示“年龄”字符串JMPagainclose:MOVAH, 3EHINT21H;关闭当前打开的文件over:.EXIT0END从例10.11和10.12,我们不难掌握记录文件的读写方法。有兴趣的读者,还可以利用文件指针的定位来指定读写某个具体的记录。10.5 动态数据的编程动态数据结构是一种常用的数据结构,在事先不知道所处理数据容量的情况,用动态数据是一种行之有效的方法,也为许多C语言程序员所采用。在汇编语言中,我们也可以采用动态数据的方式来存储数据,并进行链表的遍历。 为了使读者尽快理解本例的功能,我们把与之相似功能的C语言程序书写如下:#include #include struct link int data;struct link *next;void main( )struct link *head=NULL, *temp, *pt;int i;for (i = 20; i 0; i-) ;生成20个结点的链表temp = (struct link *)calloc(1,sizeof(struct link);申请一个结点的空间if (temp = NULL) break;若分配内存失败temp-data = i;temp-next = NULL;if (head = NULL) head = temp;else pt-next = temp;pt = temp;while (head != NULL) ;遍历结点并输出其数据字段printf(%dn,head-data);pt = head; head = head-next; free(pt);例10.13 编写一个程序用动态链表存储20,19,1,并用遍历链表的方法来显示每个结点的数值。 解:显示解答 10.6 COM文件的编程COM文件和EXE文件都是可执行文件,最典型的COM文件是Command.COM。COM文件的主要特点如下: 1、COM文件只有一个段,其字节数不会超过64K;2、当操作系统装入COM文件时,四个段寄存器(CS、DS、ES和SS)都 用PSP的段值来初始化;3、必须用伪指令ORG 100H来说明空出前256个字节。 例10.14 编写一个显示字符串“Hello”的COM类型的程序。解:CSEGSEGMENT CODEORG100H;空出前256个字节start:LEADX, MSGMOVAH, 09HINT21HMOVAX, 4C00HINT21HMSGDBHello$;定义字符串CSEGENDSENDstart对上面程序,其生成的COM文件只有23个字节,而其EXE文件的字节数会超过1K。 在PWB编程环境下,可在OptionProject TemplatesSet Project Template在列表框中选DOS COM来指定生成COM文件。在Turbo Assember系统中,可用TASM、TLINK /T来指定生成COM文件。10.7 驻留程序驻留程序TSR(Terminate but Stay Resident)是一种特殊应用程序,它在装入内存运行后,其部分代码仍然驻留在内存,当该段代码被激活时,它又进入运行状态。常用的驻留程序是作为某个中断处理程序的一部分,其激活条件就是系统产生了此中断的中断请求。 虽然驻留程序可根据具体的需要有不同的编写方式,但其典型结构包括以下几部分: 1、保存、修改中断向量表;2、程序第一次运行时的初始化部分:用自己定义的地址来取代中断向量表中的原地址确定驻留代码部分的字节数用中断21H之功能31H把需要驻留代码部分驻留在内存3、驻留内存的代码部分。例10.15 在NumLock处于“开状态”时,每按小键盘(Numeric Keypad)上的数字键,给出“啪啪”响声。解:显示解答例10.16 编写一个驻留程序,它可显示当前时间的秒数。 解:显示解答10.8 程序段前缀及其应用程序段前缀PSP(Program Segment Prefix)是一个具有256个字节的信息区,是可执行文件(EXE和COM)所特有的,其内容在操作系统装入该文件运行时存入。 10.8.1 程序段前缀的字段含义PSP信息区的字段分布如下表10.1所列。表10.1 PSP信息区的字段分布表偏移量内容含义偏移量内容含义0001H程序结束指令中断20H2E31H保留0203H分配给该程序的最后段的段地址3233H文件句柄表的长度0409H保留3437H指向文件句柄表的远指针0A0DH中断22H的地址(处理终止程序)384FH保留0E11H中断23H的地址(处理Break)5051H中断21H的功能调用1215H中断24H的地址(处理严重错误)525BH保留1617H保留5C6BH参数区1182BH缺省的文件句柄表6C7FH参数区22C2DH程序环境块的段地址80FFH存储缺省DTA的缓冲区PSP信息区的字段说明: 182BH字段:该字段内共有20个字节,每个字节存储一个文件句柄,所以,系统允许应用程序在某一时刻最多只能打开20个文件。 前5个字节存储系统标准设备的句柄,可参阅8.3.6节系统标准设备的句柄。若某文件需要同时打开更多的文件,则需要调整文件句柄数。即:先用中断21H之功能4AH释放内存,再用其功能67H来设置新的文件句柄数。MOV BX, NewNum ;新的文件句柄最大数(2065535)MOV AH, 67HINT 21H2C2DH字段:该字段存放程序运行环境的段地址,程序的缺省运行环境有160个字节,最多可达32K。该环境含有系统命令,如:COMSPEC、PATH、PROMPT和SET。 5C6BH字段:该字段存放命令行的第一个参数。假设要执行下列命令: Masm D:test.asm这时,04H(驱动器D)、8个字符的文件名和3个字符的后缀存放在该区域,没有存放字符的单元用空格(20H)来填充。若省缺驱动器和文件名,则第一个字节为00H,其它单元为20H。 6C7FH字段:该字段存放命令行的第二个参数。假设要执行下列命令: Masm D:test.asm, test.obj 这时,test.obj作为第二参数存入该字段,存储方式如上字段。 80FFH字段:该字段的第一个字节存放命令行参数的字符数,第二个字节为空格,从第三个字节开始存放命令行参数字符。10.8.2 程序段前缀的应用了解和掌握PSP中的信息分布就是为了利用其信息。对于EXE文件,可用中断21H之功能51H获取其段地址。如:MOVAH, 51HINT21H;BX=PSP的段地址MOVES, BXCMPbyte ptr ES:80H, 0;检查PSP的长度JEnext对于COM文件,因为其只有一个段,所以,可用更简单的方式来检查PSP的内容。MOVBX, 80HCMPbyte ptr BX, 0;检查PSP的长度JEnext例10.17 利用PSP中的信息来终止当前程序的运
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 高职医卫类专业群特色专业建设思路
- 公司员工安全培训知识课件
- 楼顶防水的申请报告(3篇)
- 留守儿童协议合同(标准版)
- 经纪保密合同(标准版)
- 丽水职业安全培训中心课件
- D-L-a-Aminoadipoyl-L-Cysteinyl-D-Isodehydrovaline-CoA-生命科学试剂-MCE
- 《jsp程序设计》课件
- 临邑郑城镇消防安全培训课件
- 防盗抢劫应急预案(仓库厂区)
- 高三励志课件
- 河南省人民医院2025年护士规范化培训招生考试参考题库及答案解析
- 防消联勤课件
- 绿色交通系统无人驾驶车辆示范项目可行性研究报告
- 2025年领导干部政治理论知识竞赛题库及答案
- 2025国庆中秋节前安全教育
- 读书的力量课件
- 输电线路工程冬季施工方案
- 矿山安全三级教育培训课件
- 急性上呼吸道感染课件
- 抵押协议书样板3篇
评论
0/150
提交评论