版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
5.1 子程序 5.2 参数的传递
5.3 嵌套和递归子程序
5.4 多模块程序设计 5.5 汇编语言与C语言混合编程 5.6 DOS和BIOS调用第五章 子程序IPCS指令存放在内存中,处理器从内存取指令分析执行指令一般是从前到后顺序执行的CPU执行当前指令时,IP被更改为下条指令的地址,CS和IP不能用MOV等传送指令直接修改跳转类指令可以通过修改IP和CS实现程序的不同流程本章的子程序调用和返回指令也是通过修改IP和CS实现的。回顾:专用寄存器下一条指令的段地址下一条指令的偏移地址把程序需要完成的任务分解为若干个“子任务”,每个“子任务”由一段相对独立的程序完成,称为“子程序”。子程序也称为“过程”(Procedure),在高级语言里还称作“函数”(Function)。子程序(Subroutine)概念一个简单子程序;显示回车换行的子程序dispcrlfprocnear movah,02h ;2号子功能
movdl,0dh ;回车的ascii码 int21h ;显示 movah,02h ;2号子功能
movdl,0ah ;换行的ascii码 int21h ;显示 ret;返回主程序dispcrlf endpcodesegment assumecs:codestart: calldispcrlf;调用子程序 movah,4c00h int21h ;返回DOScodeendsendstart1.程序结构清晰,提高了程序的可阅读性和可维护性。2.每个子程序可以独立地进行调试,由于程序规模较小,降低了调试难度。3.每个子程序就是一个具有特定功能的独立的程序,提高程序的“可重用性”,提高了软件开发效率。
子程序结构程序的优点
主程序:…………
CALLPROC1MOVAX,BX……
子程序PROC1………………RET把下一条指令地址压入堆栈保存按照堆栈保存的地址返回子程序的调用和返回示意近程子程序:只能在同一个代码段内调用的子程序。由于主程序和子程序处于同一个代码段,调用和返回时只需要改变IP寄存器的值,CS寄存器的值保持不变。近程子程序的入口地址用16位段内偏移地址表示。远程子程序:能够被不同代码段的程序调用。调用时,需要同时改变CS和IP寄存器的值,返回时,需要从堆栈里弹出32位的返回地址送入IP,CS寄存器。远程子程序的入口地址用16位段基址和16位段内偏移地址表示。子程序的类型5.1.1CALL和RET指令
5.1.2子程序的定义5.1.3子程序文件5.1.4子程序应用
5.1 子程序
CALL(Call,调用)指令段内直接调用 格式:CALL 子程序名操作:SP←SP-2,SS:[SP]←IP(保存16位返回地址)
IP←子程序入口的偏移地址段内间接调用 格式:CALL REG16/MEM16
操作:SP←SP-2,
SS:[SP]←IP(保护16位返回地址)
IP←REG16/MEM16
段内调用段间直接调用
格式:CALLFARPTR 子程序名操作: SP←SP-2,SS:[SP]←CS SP←SP-2,SS:[SP]←IP
(以上是保存32位返回地址,偏移地址保存在较小地址处)
IP←子程序入口的偏移地址,
CS←子程序入口的段基址段间间接调用格式:CALL MEM32操作:SP←SP-2, SS:[SP]←CS SP←SP-2, SS:[SP]←IP IP←[MEM32],CS←[MEM32+2]
段间调用(1) CALL PROC1(2) LEA CX, PROC1 CALL CX (3)ADDR_PROC1DWPROC1;子程序偏移地址 …… CALL ADDR_PROC1 ;调用近程子程序PROC1(4) LEA BX,ADDR_PROC1 CALL WORDPTR[BX] ;调用近程子程序PROC1调用子程序的四种方法无参数段内返回 格式:RET
操作:IP←SS:[SP],SP←SP+2
有参数段内返回 格式:RETD16 ;其中D16代表16位数据 操作:IP←SS:[SP],SP←SP+2
SP←SP+D16
无参数段间返回
格式:RET
操作: IP←SS:[SP],SP←SP+2 CS←SS:[SP],SP←SP+2有参数段间返回 格式:RETD16
;其中D16代表16位数据 操作: IP←SS:[SP],SP←SP+2 CS←SS:[SP],SP←SP+2
SP←SP+D16
RET指令5.1.2子程序的定义
子程序名 PROC [NEAR/FAR]
;子程序体
RET子程序名
ENDP
子程序名应该是合法的标识符。用NEAR说明的是“近程子程序”,只能在同一代码段内调用。用FAR说明的是“远程子程序”,能在其它代码段调用。子程序的远近属性选项可以省略(省略时为近程子程序)子程序的定义要写在代码段内[例5.1]子程序FRACTOR用来计算一个数的阶乘。主程序利用它
计算1~5的阶乘,存入FRA数组。
DATA SEGMENT FRADW5DUP(?)DATA ENDSCODE SEGMENT ASSUMECS:CODE,DS:DATASTART:MOV AX,DATA MOV DS,AX MOV BX,1 ;BX中存放待求阶乘的数 MOVSI,0 MOV CX,5 ;求阶乘次数(循环次数)LOOP0:CALL FRACTOR ;调用FRACTOR求阶乘 MOV FRA[SI],AX ;保存结果(阶乘) ADD SI,2 INC BX ;产生下一个待求阶乘的数 LOOP LOOP0 ;循环控制 MOV AX,4C00H INT 21HCODE ENDS END START思考题:这个程序正确么?;输入参数:BX:对哪个数求阶乘;输出参数:AX:结果FRACTOR PROCNEAR MOV CX,BX ;待求阶乘的数转入CX寄存器 MOV AX,1 ;“累乘器”置初值1FRALOOP: MUL CX ;累乘 LOOP FRALOOP ;循环控制 RETFRACTOR ENDPFRACTOR PROCNEAR PUSH CX ;CX压入堆栈保护
MOV CX,BX ;待求阶乘的数转入CX寄存器 MOV AX,1 ;“累乘器”置初值1FRALOOP: MUL CX ;累乘 LOOP FRALOOP ;循环控制 POP CX ;从堆栈里弹出CX的原值
RETFRACTOR ENDP
子程序名 PROC [NEAR/FAR] PUSH …… ;保护现场(寄存器/存储器) PUSH …… ;个数根据具体情况决定 …… ;子程序主体 …… POP …… ;恢复现场,注意出栈次序 POP …… ;先进栈的寄存器后出栈 RET ;返回
子程序名 ENDP在子程序入口处把相关寄存器的值入栈保护,程序返回前再恢复它们的值,这个操作称为“保护现场”和“恢复现场”。子程序的一般格式5.1.3子程序文档设计一个子程序之前,首先应该明确:子程序的名字;子程序的功能;入口参数:为了运行这个子程序,主程序为它准备了哪几个“已知条件”?这些参数存放在什么地方?出口参数:这个子程序的运行结果有哪些?存放在什么地方?影响寄存器:执行这个子程序会改变哪几个寄存器的值?其它需要说明的事项。
上述内容连同子程序源代码等合称为“子程序文档”。常常把上述内容以“程序注释”的方式书写在一个子程序的首部。给使用者提供调用信息。;名称:Square;功能:求16Bit无符号数的平方根;入口参数:16Bit无符号数在AX中;出口参数:8Bit平方根数在AL中;影响寄存器:AX(AL);方法:利用公式:N^2=1+3+……+(2N-1)求平方根SQUARE PROC NEAR PUSH CX ;保护现场 PUSH BX MOV BX,AX ;要求平方根的数送BX MOV AL,0 ;AL中存放平方根,初值0
MOV CX,1 ;CX置入第一个奇数1
;利用公式:N^2=1+3+……+(2N-1)求平方根NEXT: SUB BX,CX JB DONE ADD CX,2 ;形成下一个奇数 INC AL ;AL存放已减去奇数的个数 JMP NEXTDONE: POP BX ;恢复现场 POP CX RET ;返回SQUARE ENDP“SQUARE”的子程序,求一个数的平方根5.1.4子程序应用每调用一次子程序,主程序需要做三件事:(1)为子程序准备入口参数(2)调用子程序(3)处理子程序的返回参数DATA SEGMENT X DW 59,3500,139,199,77 ;欲求平方根的数组 ROOT DB 5DUP(?) ;存放平方根内存区DATA ENDSCODE SEGMENT ASSUME CS:CODE,DS:DATASTART:MOV AX,DATA MOV DS,AX
LEA BX,X ;初始化指针 LEA SI,ROOT MOV CX,5 ;设置计数器初值ONE: MOV AX,[BX]
;设置入口参数 CALL SQUARE ;调用子程序
MOV [SI],AL
;保存返回参数(平方根) ADD BX,2 ;修改指针 INC SI ;修改指针 LOOP ONE ;循环控制
MOV AX,4C00H ;返回DOS INT 21H[例]求5个无符号数的平方根。子程序;名称:Square;功能:求16Bit无符号数的平方根;入口参数:16Bit无符号数在AX中;出口参数:8Bit平方根数在AL中;影响寄存器:AX(AL);方法:利用公式:N^2=1+3+……+(2N-1)求平方根SQUARE PROC NEAR PUSH CX ;保护现场 PUSH BX MOV BX,AX ;要求平方根的数送BX MOV AL,0 ;AL中存放平方根,初值0
MOV CX,1 ;CX置入第一个奇数1NEXT: SUB BX,CX JB DONE ADD CX,2 ;形成下一个奇数 INC AL ;AL存放已减去奇数的个数 JMP NEXTDONE: POP BX ;恢复现场 POP CX RET ;返回SQUARE ENDPCODEENDS ENDSTART5.2 参数的传递 传递的参数有两种类型:(1)值传递:把参数的值放在约定的寄存器或存储单元传递给子程序,或者,由子程序返回给主程序。
如果一个入口参数是用值传递的,子程序使用这个值,但是不改变它自身的值。(2)地址传递:把参数事先存放在某个存储单元,把这个存储单元的地址作为参数传递给子程序。
如果一个参数使用它的地址来传递,子程序可以改变这个参数的值。例如,把存放结果的存储单元的地址作为“入口参数”传递给子程序,子程序可以把运算结果直接存入这个单元。利用寄存器传递参数利用主、子程序“共享”的数据段传递参数利用堆栈传递参数三种常用的参数传递方法:
用寄存器传递参数
[例5.3] 求菲波那契数列的前n项。分析:菲波那契数列的前两项为1,1,以后的每一项都是其前两项之和。 X0=1, X1=1, Xi=Xi-1+Xi-2(i>=2)。方法1:利用寄存器传递参数的值。方法2:利用寄存器传递参数的地址。.MODEL SMALL.DATA FIBLST DW 1,1,18DUP(?) N DW 20.CODESTART: MOV AX,@DATA MOV DS,AX LEA SI,FIBLST MOV CX,N SUB CX,2ONE: MOV AX,[SI] MOV BX,[SI+2] CALL FIB MOV [SI+4],AX ADD SI,2 LOOP ONE MOV AX,4C00H INT 21H方法1:利用寄存器传递参数的值。;子程序FIB;功能:求菲波那契数列数列的一项;入口参数:AX=Xi-1,BX=Xi-2;出口参数:AX=Xi=Xi-1+Xi-2FIB PROC ADD AX,BX RETFIB ENDP END START.MODEL SMALL.DATA FIBLST DW 1,1,18DUP(?) N DW 20 .CODESTART:MOV AX,@DATA MOV DS,AX LEA SI,FIBLST MOV CX,N SUB CX,2ONE: CALL FIB ADD SI,2 LOOP ONE MOV AX,4C00H INT 21H方法2:利用寄存器传递参数的地址。;子程序FIB;功能:求菲波那契数列数列的一项;入口参数:SI=Xi-2的段内偏移地址;出口参数:无(结果已由子程序存入数组内)
FIB PROC PUSH AX MOV AX,[SI] ;取XI-2 ADD AX,[SI+2] ;加XI-1 MOV [SI+4],AX ;存入XI POP AX RETFIB ENDP END START .MODEL SMALL .DATA FIBLSTDW 1,1,18DUP(?) N DW 20 XI1 DW ?;存放XI-1 XI2 DW ?;存放XI-2 XI DW ?;存放XI .CODE START:MOV AX,@DATA MOV DS,AX
LEA SI,FIBLST;设置地址指针 MOV CX,N SUB CX,2 ;设置计数器初值ONE: MOV AX,[SI+2]
MOV XI1,AX
;Xi-1置入XI1
MOV AX,[SI] MOV XI2,AX
;Xi-2置入XI2 CALL FIB ;调用子程序
MOV AX,XI ;取出计算结果Xi MOV [SI+4],AX;置入FIBLST数组 ADD SI,2 ;修改地址指针 LOOP ONE ;循环控制 MOV AX,4C00H INT 21H2.变量(共享数据段)传递参数
;子程序FIB;功能:计算菲波那契数列数列的一项;入口参数:XI1=XI-1,XI2=XI-2;出口参数:XI=XI-1+XI-2FIB PROC PUSH AX ;保护现场 MOV AX,XI1 ;AX=XI-1 ADD AX,XI2 ;AX=XI-1+XI-2 MOV XI,AX ;XI←AX POP AX ;恢复现场 RETFIB ENDP END START26.MODEL SMALL.DATA FIBLSTDW1,1,18DUP(?) N DW 20 .CODESTART: MOVAX, @DATA MOV DS, AX LEA SI, FIBLSTMOV CX, N SUB CX, 2ONE:PUSH AX
;为保存结果,在堆栈“预留”单元
PUSH WORDPTR[SI]
;XI-2入栈
PUSH WORDPTR[SI+2]
;XI-1入栈
CALL FIB ;调用子程序,执行后堆栈状态1
POP AX
;从堆栈弹出结果,执行后堆栈状态4
MOV [SI+4],AX
;把结果存入FIBLST数组 ADD SI,2 LOOP ONE MOV AX,4C00H INT 21H3.堆栈传递参数;子程序FIB;功能:计算菲波那契数列数列的一项;入口参数:XI-1,XI-2在堆栈中;出口参数:XI在栈顶FIB PROC PUSH BP MOV BP,SP ;堆栈状态2PUSHAX MOV AX,[BP+4] ;从堆栈取出XI-1 ADD AX,[BP+6] ;AX=XI-1+XI-2 MOV [BP+8],AX ;结果存入堆栈POPAX POP BP ;恢复BP RET 4
;返回,SP+4,执行后堆栈状态3FIB ENDP END START思考:1。修改参数压栈的顺序,程序如何修改?2。改用堆栈传入参数,寄存器返回参数,程序如何修改?5.3 嵌套和递归子程序5.3.1 子程序嵌套
5.3.2递归子程序5.3.1 子程序嵌套 被调用的子程序在返回前又调用了其他子程序,称为子程序嵌套。
(也称过程嵌套NestProcedureCall);主程序 ……. CALL SUBA ;① …….;子程序SUBA
……. CALL SUBB ;② ……. RET;子程序SUBB ……. RET5.3.2递归子程序递归过程(RecursiveProcedure):一个直接或间接调用自身的过程。 如果一个问题可以分解为几个子问题,而这些子问题又和原问题有相同的算法时,递归过程是非常方便的表达方式。 递归过程可以使用堆栈传递参数,也可以用寄存器传递参数或存储单元传递参数。计算1~N自然数之和可以定义为:SUM(N)= N=1时,SUM函数直接返回1。 N>1时,计算SUM(N)分成两步: (1)计算SUM(N-1):设置入口参数为N-1,
调用SUM函数(调用自身) (2)进行加法运算:N+SUM(N-1)也可以定义为:SUM(N)=1+2+3+……+N.MODEL SMALL.STACK 100 ;定义100字节用作堆栈.CODETOTAL DW ?START:
MOV CX,3 ;为子程序准备参数 CALL SUM ;调用子程序SUM
MOV CS:TOTAL,AX;保存结果 MOV AX,4C00H INT 21H ;返回;递归过程SUM;入口参数:CX=N,出口参数:AX=1~N的和SUM PROC PUSH CX ;保存入口参数(N)
CMP CX,1 ;判CX(N)是否为1 JE BACK ;CX(N)为1,转BACK DEC CX ;CX-1,为下一次调用准备参数 CALL SUM ;递归调用,计算1~N-1的和 INC CX ;恢复CX为N ADD AX,CX ;加到AX中 JMP EXITBACK: MOV AX,1 ;N=1时,通过AX返回1EXIT: POP CX ;恢复入口参数 RETSUM ENDP END START
这个程序使用寄存器进行参数的传递。主程序中的“CALLSUM”指令执行了一次。子程序SUM中的指令“CALL SUM”执行了2次。CALL指令先后共执行了3次,先后3次进入同一个子程序
SUM。递归调用和返回示意图编写递归程序时,必须保证要有一个终止条件,而且,经过有限次执行这个子程序,这个终止条件能够实现。否则,程序将产生“死循环”或者其它的错误状态。.MODEL SMALL.STACK 100 ;定义100字节用作堆栈.CODETOTAL DW ?START: PUSH AX ;为返回结果预留单元, ;AX中内容无意义 MOV CX,3 ;为子程序准备参数
PUSH CX ;入口参数压入堆栈 CALL SUM ;调用子程序SUM
POP CS:TOTAL ;③保存结果 MOV AX,4C00H INT 21H ;返回 高级语言中的递归程序普遍使用堆栈传递参数。上面的例子改写如下:;递归过程SUM;入口参数:[SP+2]=N,出口参数:1~N的和在堆栈栈顶;影响寄存器:BP,AX,CXSUM PROC
PUSH BP MOV BP,SP ;根据BP访问堆栈中的参数PUSHAXPUSHCX MOV CX,[BP+4] ;取出入口参数N,存入CX CMP CX,1 ;N=1? JE BACK ;N=1,转BACK DEC CX ;CX-1,为下一次调用准备参数
PUSH AX ;为保留下一次调用结果预留单元,AX中内容无意义 PUSH CX ;入口参数(N-1)压入堆栈 CALL SUM ;递归调用,计算1~N-1的和
POP AX ;从堆栈弹出子程序运算结果(1~N-1的和) ADD AX,[BP+4] ;把N加到AX中,得到1~N的和 MOV [BP+6],AX ;1~N的和存入堆栈预留单元 JMP EXITBACK:MOV WORDPTR[BP+6],1 ;N=1时,返回1EXIT:POPCXPOPAXPOP BP RET 2SUM ENDP END START例:将字符串反序输出
movbx,offsetstr
pushbx
callrevers
popbxmovdl,[bx]movah,2int21hreversprocnearpushbpmovbp,spmovbx,[bp+4]moval,[bx]cmpal,'$'jereturnincbx
pushbx
callrevers
popbxmovdl,[bx]movah,2int21hreturn:
popbpret;不能写成:ret2(?)reversendp上机4:按照学号的顺序输入20个学生的成绩(0~100)存入一维数组SCORE,然后统计每一个学生的名次并在屏幕输出。要求:1、显示形式为“学号:名次”;2、输入每个成绩、统计每个成绩的名次、显示结果数据(学号或名次)分别定义为子程序;3、传参方式:寄存器或堆栈。5.4 多模块程序设计
5.4.1 段的完整定义5.4.2简化段定义
5.4.3创建多模块程序1.一个应用程序的开发由一个小组,而不是一个人完成的。
每个程序员编制的源程序自然地构成一个(或者是多个)源程序文件。
每个源程序文件内包括了一个或多个“子程序”。这些源程序文件常常被称为模块(Module)。
有时候也把位于同一个源程序文件内的各个“子程序”,或者一个“数据段”定义等称为模块。
一个可供实际使用的应用程序,它的源程序基本上都是由若干个程序文件组成的。2.源程序文件规模过大,会造成管理,维护上的困难。
即便是一个人开发的应用程序,也应把一个大的程序分解成多个源代码文件。
这样,每个文件长度适中,容易阅读和维护,如果修改了某个模块,只需对该模块重新汇编,然后再和其他模块链接,无须把每个模块重新汇编一次。
3.许多“子程序”可以重复使用,独立为一个源程序文件便于“子程序库”的管理。
5.4.1 段的完整定义段名 SEGMENT[对齐方式][组合方式][使用类型][‘类’] 语句段名 ENDS完整的段定义格式如下:方括号括起来的内容称为“可选项”,可以由使用者根据需要决定。1.对齐方式 一个新的段开始时,对齐方式通知连接程序怎样确定这个段的起始地址。对齐方式可以有下面几种选择:BYTE 使本段从前面段结束之后的下一个字节地址开始。WORD 使本段从前面段结束之后的下一个字地址开始。DWORD 使本段从前面段结束之后的下一个双字地址开始。PARA 使本段从前面段结束之后的下一个节的地址(16的整数倍)开始存放,是默认的对齐方式。如果没有出现对齐方式说明,自动按照“PARA”来对齐。PAGE 使本段从前面段结束之后的下一个页地址(256的整数倍)开始。
对齐方式段基址起始偏移地址段间空隙(字节)BYTE3020H0005H0WORD3020H0006H1DWORD3020H0008H3PARA3021H0000H11PAGE3030H0000H251
假设前面一个段的结束地址为30204H,用不同对齐方式的结果如下表所示。2. 组合方式 组合方式通知连接程序以何种方式处理不同程序文件中名称相同的段。组合方式可以有下面几种选择:PRIVATE 这个段不与其他同名段合并,每个段都有自己的段基地址,是默认的组合方式。PUBLIC 将该段和其他名称相同,组合方式同为“PUBLIC”的段前后连接在一起,合并成一个段,产生一个新的段基地址,所有标号的偏移地址都进行调整。STACK 将所有的STACK段连接为一个新的STACK段,类似于PUBLIC。COMMON所有同名的段使用相同的起始地址,这样,这些段“共享”这一片共同的存储区间。AT表达式 段的起始地址由表达式的值来指定,用于设定一些特殊的段。使用(USE)类型使用类型仅仅使用于使用80386以上指令系统的汇编程序(例如,在程序首部使用.386说明符)使用16位寻址方式时,段内偏移地址16位,每个段最大64KB。运行在实模式下的程序使用16位段。使用32位寻址方式时,段内偏移地址为32位,一个段最大可达4GB。运行在保护模式下的程序使用32位段。使用80386以上CPU并且工作在实模式下的程序不能遗漏“USE16”说明。
例如:mulax,5imulax,bx,-14.类名称 一个段除了有一个段名之外,还可以有一个类名称。类名称是以引号引起来的任意字符串。 类名称相同的段被放在一片相邻的存储区间,但不会合并成同一个段。
CSEGSEGMENT 'CODE'ASSUMECS:CSEG,DS:DATA1ASSUMESS:MYSTACKMAIN PROC MOV AX,DATA1 MOV DS,AX MOV AX,SEGVAL2 MOV ES,AX
MOV AX,VAL1 LEA SI,VAL2 MOV BX,ES:[SI] CMP AX,BX JB L1 CALL FARPTRSUB1L1: MOV AX,4C00H INT 21HMAIN ENDPCSEG ENDS
[例5.5]本例中有两个代码段、两个数据段和一个堆栈段,SUBCODE SEGMENT ‘CODE’ ASSUMECS:SUBCODE,DS:DATA2,SS:MYSTACKSUB1 PROC FAR
; 过程体 RETSUB1 ENDPSUBCODE ENDS;==================================
DATA1 SEGMENT 'DATA';类名称为DATA VAL1 DW 1001HDATA1 ENDS;=============================
DATA2 SEGMENT 'DATA’;类名称为DATA VAL2 DW 1002HDATA2 ENDS;=============================MYSTACK SEGMENTPARASTACK'STACK' DB 100HDUP(?)MYSTACK ENDS;=============================
END MAIN52 经过链接,从对应的MAP文件中可以看到程序有两个代码段、两个数据段和一个堆栈段:Start Stop Length NameClass00000H 00022H 00023H CSEGCODE00030H 00030H 00001H SUBCODECODE00040H 00041H 00002H DATA1DATA00050H 00051H 00002H DATA2DATA00060H 0015FH 00100H MYSTACKSTACK由于使用默认的“PARA”对齐方式,段间有间隙。同类型名的段相邻存放。如果将SUBCODE的段名改为CSEG,将DATA2段名改为DATA1,则链接器创建的MAP文件是这样的:Start Stop Length NameClass00000H00023H 00024H CSEGCODE00030H00033H 00004H DATA1DATA00040H0013FH 00100H MYSTACKSTACK由此可见,在同一个程序文件中,相同段名的代码段合并成一个段,两个同名的数据段也合并成一个段。5.4.2 简化段定义
1.内存模式定义伪指令使用简化段定义时,需要首先定义所使用的内存模式等参数。格式如下:.MODEL内存模式[,语言类型][,操作系统类型][,堆栈选项]
2.近数据段定义伪指令 .DATA.DATA? .CONST.DATA创建一个数据段,段名是_DATA,主要定义有初值的变量。.DATA?用来定义没有初值的变量,段名为_BSS。定义在.DATA?段的无初值变量不占用可执行文件的空间。.CONST定义只读数据段,段名是_CONST。一个源文件中可以包含多个由.DATA、.DATA?和.CONST定义的数据段,TASM自动将其合并为一个物理段,使它们具有相同的段基址。3.远数据段定义伪指令
.FARDATA[段名]和.FARDATA?[段名] 分别用来定义已有初值的数据段和没有初值的数据段。如果段名缺省,默认段名分别为FAR_DATA和FAR_BSS。通过指定段名,可在源文件中定义多个独立的数据段。远数据段不会被合并。
4.代码段定义伪指令 .CODE [段名]
如果内存模式为SMALL,.CODE伪指令不需要给出“段名”,编译器生成一个名为_TEXT的代码段,对于多个代码段的内存模式,则应该指明段名。
5.堆栈段定义伪指令 .STACK [堆栈大小]定义一个堆栈段,缺省大小为1024字节,段名为STACK。6.TASM预定义符号 @CODE和@DATA@CODE表示由.CODE定义的代码段的段名,在指令中表示段基址。@DATA表示由.DATA、.DATA?和.CONST定义的数据段的段名,在指令中表示段基址。注意:使用简化段定义时,没有段结束伪指令。一个新的段开始,就意味着上一个段到此结束。
.MODEL MEDIUM;
内存模式为中型.STACK 100H.DATAMSG1 DB"FIRSTMESSAGE”,0DH,0AH,"$".DATA? ;定义第二个数据段,未初始化 X DB 10DUP(?).CODEMAIN PROC MOV AX,@DATA MOV DS,AX LEA DX,MSG1 CALL FARPTRDISP ;远调用 MOV AX,4C00H INT 21HMAIN ENDP[例5.6] 本例中有两个代码段。.CODE OTHER;再定义一个代码段DISPPROCFAR MOV AH,9 INT 21H RETDISP ENDP ENDMAIN ;入口地址MAIN59经过链接,从对应的MAP文件中可以看到程序有两个代码段、两个数据段和一个堆栈段:Start StopLength Name Class00000H00011H00012HEX506_TEXT CODE00012H00016H00005HOTHER CODE00020H0002FH00010H_DATA DATA00030H0012FH00100HSTACK STACK00130H00130H00001H_BSS BSS用TD观察这个程序,发现_DATA和_BSS段使用相同的段基址,被合并为一个物理段。5.4.3 创建多模块程序
模块
:由不同的程序员编写,以不同的文件名存放在磁盘中,这样的源程序文件称为“模块”
模块内部由若干个段的定义组成,其中仅有一个模块包含有主程序,其它的模块包含了一些子程序和数据段、堆栈段等。
几个源程序文件在连接、合并成为一个完整的可执行程序时需要解决两个问题:不同源程序文件中的段如何排列、组合?一个源程序文件怎样调用/访问位于另一个文件中的子程序、变量?
同名段的组合方式: 组合方式通知连接程序以何种方式处理不同程序文件中名称相同的段。组合方式可以有下面几种选择:PRIVATE 这个段不与其他同名段合并,每个段都有自己的段基地址,是默认的组合方式。PUBLIC 将该段和其他名称相同,组合方式同为“PUBLIC”的段前后连接在一起,合并成一个段,产生一个新的段基地址,所有标号的偏移地址都进行调整。STACK 将所有的STACK段连接为一个新的STACK段,类似于PUBLIC。COMMON所有同名的段使用相同的起始地址,这样,这些段“共享”这一片共同的存储区间。AT表达式 段的起始地址由表达式的值来指定,用于设定一些特殊的段。 为了使模块B中的子程序、变量能够被外部模块访问,在模块B中用PUBLIC伪指令申明相应的子程序或变量。 模块A为了能够访问在模块B中的子程序或者变量,要用EXTRN伪指令申明要调用的过程名或变量名是外部的。;模块aextrnx:byteextrndisp:farcode1segment … movx,6 calldisp …code1ends;模块bpublicxpublicdispdatasegment xdb5dataendscode2segmentdispprocfar …dispendp
code2ends
63两条伪指令的格式如下:EXTRN 符号名1:TYPE[,……]如果是变量,则TYPE应该是BYTE、WORD、DWORD等;如果标号或子程序名,则TYPE为NEAR或FAR。PUBLIC 符号名1,符号名2,…
本模块可以被其它模块引用子程序名、标号、变量名。
[例5.7] 计算ARRAY数组元素之和,存放到SUM单元。;MODULA.ASM
;模块A源程序EXTRNARRAYSUM:FAR
;声明ARRAYSUM是外部函数,将在本模块中被调用PUBLICARRAY
;声明全局变量,可供其他模块使用DSEG1 SEGMENTPUBLIC
ARRAY DW 2,4,6,8 LENTH EQU ($-ARRAY)/2DSEG1 ENDSDSEG2 SEGMENT SUM DW ?DSEG2 ENDSCSEG SEGMENT ASSUME CS:CSEG,DS:DSEG2 MAIN PROC FAR MOV AX,DSEG2 MOV DS,AX
MOV CX,LENTH
CALL ARRAYSUM;调用外部函数
MOV SUM,AX MOV AX,4C00H INT 21H MAIN ENDPCSEG ENDS
END MAIN
65;MODULB.ASM
模块B源程序EXTRNARRAY:WORD
;声明外部变量PUBLICARRAYSUM
;该模块没有数据段STAK SEGMENT 'STACK' DW 10DUP(?)STAK ENDS
CSEG SEGMENT ASSUME CS:CSEGARRAYSUM PROC FAR PUSH DS ;保留原DSPUSH SI MOV AX,SEGARRAY
;把外部变量的段基址放入 MOV DS,AX ;段寄存器DS
MOV AX,0 LEA SI,ARRAYL1: ADD AX,[SI] ADD SI,2 LOOP L1 POP SI POP DS ;恢复原DS RET ARRAYSUM ENDPCSEG ENDS
END ;模块B不需要填写“入口地址”注:两个模块中的代码段同名,但在内存中对应着两个不同的逻辑段,具有不同的段地址。方法2:;MODULA.ASM
;模块A源程序EXTRNARRAYSUM:FAR ;声明ARRAYSUM是外部函数,将在本模块中被调用PUBLICARRAY ;声明全局变量,可供其他模块使用DSEG1 SEGMENTPUBLIC ;和其它模块中的同名逻辑段合并,共用相同的段地址 ARRAY DW 2,4,6,8 LENTH EQU ($-ARRAY)/2DSEG1 ENDSDSEG2 SEGMENT SUM DW ?DSEG2 ENDSCSEG SEGMENT ASSUME CS:CSEG,DS:DSEG2 MAIN PROC FAR MOV AX,DSEG2 MOV DS,AX MOV CX,LENTH CALL ARRAYSUM;调用外部函数 MOV SUM,AX MOV AX,4C00H INT 21H MAIN ENDPCSEG ENDS END MAIN67;M
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025-2030年中国手机镜头行业并购重组扩张战略制定与实施研究报告
- 2025-2030年中国LED 驱动芯片行业营销创新战略制定与实施研究报告
- 2025-2030年中国北斗卫星手表行业商业模式创新战略制定与实施研究报告
- 2025-2030年中国中餐行业开拓第二增长曲线战略制定与实施研究报告
- 市政道路竣工验收质量评估报告-定稿
- 建设项目环境保护设施竣工验收程序及说明-(空白表)
- 者楼镇高洛小学文明礼仪实施方案
- 化纤高档服装项目可行性研究报告
- 医疗器械定期风险评价报告范文
- 2022-2027年中国血管舒缓素行业发展监测及投资战略咨询报告
- 小学六年级数学100道题解分数方程
- GB/T 17684-2008贵金属及其合金术语
- 安全管理流程图加强完善版
- 第一讲-研发创新型企业需要IPD(下)徐骥课程-
- 2022年08月北京外交学院非事业编科研助理招聘14人高频考点卷叁(3套)答案详解篇
- 甲状腺结节的超声规范化诊断教学课件
- 职业健康监护技术规范
- 安徽省白酒生产企业名录395家
- 多媒体技术与应用ppt课件(完整版)
- 2022年五年级数学兴趣小组活动记录
- 阅读题赊小鸡
评论
0/150
提交评论