实验三 编程实现子程序调用
- 格式:doc
- 大小:74.00 KB
- 文档页数:6
子程序调用编程实例以子程序调用编程实例为题,本文将介绍什么是子程序,以及如何使用子程序来编写更加高效和易于维护的代码。
子程序是指在程序中定义的独立的程序段,它可以被其他程序调用。
子程序可以接收输入参数,执行特定的处理逻辑,并返回结果给调用者。
使用子程序的好处是可以将程序的功能划分成多个独立的小模块,每个模块只负责特定的处理逻辑,使程序结构更加清晰,易于理解和维护。
下面以Python语言为例,介绍如何使用子程序来实现一个简单的计算器程序。
首先定义一个名为add的子程序,用于实现两个数相加的功能。
```def add(x, y):return x + y```该子程序接收两个参数x和y,返回它们的和。
现在可以在主程序中调用该子程序,如下所示:```a = 3c = add(a, b)print(c)```该程序将输出7,即3和4的和。
可以看到,在主程序中调用子程序的方式非常简单,只需要传递参数并接收返回值即可。
接下来再定义一个名为subtract的子程序,用于实现两个数相减的功能。
```def subtract(x, y):return x - y```现在可以在主程序中调用这两个子程序,实现一个完整的计算器程序,如下所示:```a = 3b = 4c = add(a, b)d = subtract(a, b)print(d)```该程序将输出7和-1,即3和4的和以及差。
可以看到,使用子程序可以使程序结构更加清晰,易于理解和维护。
除了上述简单的例子,子程序还可以用于实现更加复杂的功能,比如排序、查找等。
使用子程序可以大大提高程序的可读性和可维护性,使程序员可以更加专注于实现业务逻辑,而不必过多关注程序的结构和实现细节。
子程序是编写高效和易于维护的程序的重要手段之一。
程序员可以将程序的功能划分成多个独立的小模块,并通过调用子程序的方式来实现这些功能。
这样可以使程序结构更加清晰,易于理解和维护,提高程序的可读性和可维护性。
子程序存储在存储器中,可供一个或多个调用程序(主程序)反复调用。
主程序调用子程序时使用CALL指令,由子程序返回主程序时使用RET指令。
由于调用程序和子程序可以在同一个代码段中,也可以在不同的代码段中,因此,CALL指令和RET指令也有近调用、近返回及远调用、远返回两类格式。
⑴ CALL NEAR PTR <子程序名> 近调用(near call) 近调用是CALL指令的缺省格式,可以写为"CALL <子程序名>rotine"。
它调用同一个代码段内的子程序(子过程),因此,在调用过程中不用改变CS的值,只需将子程序的地址存入IP寄存器。
CALL指令中的调用地址可以用直接和间接两种寻址方式表示。
⑵ CALL FAR PTR <子程序名> 远调用(far call) 远调用适用于调用程序(也称为主程序)和子程序不在同一段中的情况,所以也叫做段间调用。
和近调用指令一样,远调用指令中的寻址方式也可用直接方式和间接方式。
⑶ RET 返回指令(return) RET指令执行的操作是把保存在堆栈中的返回地址出栈,以完成从子程序返回到调用程序的功能。
● CALL <子程序名> 段内直接调用 执行操作:① (SP) ← (SP)-2,((SP)) ← (IP)当前 ② (IP) ← (IP)当前+16位位移量(在指令的第2、3个字节中) ● CALL DESTIN 段内间接调用 执行操作:① (SP) ← (SP)-2,((SP)) ← (IP)当前 ② (IP) ← (EA) ; (EA)为指令寻址方式所确定的有效地址 ● CALL FAR PTR <子程序名> 段间直接调用 执行操作:① (SP) ← (SP)-2,((SP)) ← (CS)当前 (SP) ← (SP)-2,((SP)) ← (IP)当前 ② (IP) ←偏移地址(在指令的第2、3个字节中) (CS) ←段地址(在指令的第4、5个字节中) ● CALL WORD PTR DESTIN 段间间接调用 执行操作:① (SP) ← (SP)-2,((SP)) ← (CS)当前 (SP) ← (SP)-2,((SP)) ← (IP)当前 ② (IP) ← (EA) ; (EA)为指令寻址方式所确定的有效地址 (CS) ← (EA+2) 从CALL指令执行的操作可以看出,第一步是把子程序返回调用程序的地址保存在堆栈中。
实验三子程序实验一.实验目的1.掌握主程序与子程序之间的调用关系及调用方法;2.掌握子程序调用过程中近程调用与远程调用的区别;3.掌握通过堆栈转送参数的方法。
二.实验内容1.将BUF开始的10个单元中的二进制数转换成两位十六进制数的ASCII码,在屏幕上显示出来。
要求码型转换通过子程序HEXAC实现,在转换过程中,通过子程序DISP实现显示。
2.编写一个主程序,从键盘接收若干个字符,然后用远调用的方法,调用子程序统计字符串中字符’b’的个数.子程序的参数是字符串的首地址TABLE,字符串长度N及字符”b”.子程序返回字符"b”的个数.参数传送采用堆栈实现.主程序在子程序返回后,显示字符”b”及其个数(设为一位十六进制数)。
三.实验要求1.第一个实验程序用子程序的近程调用实现。
由于在调用HEXASC子程序时,子程序又调用了DISP子程序,这叫子程序的嵌套调用。
实验过程中可以从堆栈的内容看到两个子程序的返回地址值。
由于是近调用,地址值只包括返回地址的段内偏移量。
在每个子程序的执行中,检查CS值是不变的。
2.第二个程序是利用远调用的方法调用子程序的。
在远调用情况下,主程序与子程序处在不同的逻辑代码段中,可在子程序执行中查看CS值,它与主程序中的CS值是不同的。
子程序调用后,堆栈中保留了返回地址的段地址及段内偏移量。
3.第二个程序中,主程序与子程序之间参数的传送是由堆栈实现的。
一段是将参数(此处是串首址TABLE,串的长度N及待统计的字符“b”)顺序压如堆栈,在子程序调用后,通过BP指针对堆栈中的参数访问,并将统计的结果通过堆栈返回。
有关该方法的原理此处不再介绍。
4.预习子程序设计的基本方法,根据实验内容要求,画出子程序及主程序的流程图;熟悉键盘键入字符串及用堆栈传送参数的程序段编制方法。
四.实验环境PC微机DOS操作系统或Windows 操作系统MASM.EXE,LINK.EXE,或宏汇编集成环境五.实验步骤1.编辑、汇编两个源程序,生成相应的可执行文件(.EXE)实验一;CONV.ASMDATA SEGMENTBUF DB 0ABH,0CDH,0DEH,01H,02H,03H DB 3AH,4BH,5CH,6FHDATA ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA START:MOV AX,DATAMOV DS,AXMOV CX,10LEA BX,BUFAGAIN:MOV AL,[BX]CALL HEXASCINC BXLOOP AGAINMOV AH,4CHINT 21HHEXASC PROC NEARMOV DL,ALPUSH CXMOV CL,4SHR DL,CLPOP CXCALL DISP ;显示高位HEX数MOV DL,ALAND DL,0FHCALL DISPRETHEXASC ENDPDISP PROPCMP DL,9JBE NEXTADD DL,7NEXT:ADD DL,30HMOV AH,2INT 21H ;显示RETDISP ENDPCODE ENDSEND START修改符号再次修改实验二;COUNTER.ASM DATA SEGMENT CHAR DB ‘b’BUF DB 50H,? ,50H DUP(?)DATA ENDSMCODE SEGMENTASSUME CS:MCODE,DS:DATA START:MOV AX,DATAMOV DS,AXLEA DX,BUFMOV AH,0AHINT 21HMOV DL,0AHMOV AH,02INT 21HLEA SI ,BUFMOV CL,[SI+1]MOV CH,0 ;CX中为字符串长度INC SIINC SI ;SI指向串首址TABLEMOV AL,CHARMOV AH,0 ;AX中为待查字符PUSH SIPUSH CXPUSH AX ;参数送堆栈CALL FAR PTR CHECKPOP AX ;统计个数在AL中PUSH AXMOV DL,ALAND DL,0FHCMP DL,9JBE NEXTADD DL,7NEXT:ADD DL,30HMOV AH,2INT 21H ;显示统计个数MOV AH,4CHINT 21HM CODE ENDSSCODE SEGMENTASSUME CS:SCODECHECK PROC FARPUSH BPMOV BP,SPMOV SI,[BP+10]MOV CX,[BP+8]MOV AX,[BP+6]XOR AH,AHAGAIN:CMP AL,[SI]JNE NEXT1INC AHNEXT1:INC SILOOP AGAINMOV AL,AHMOV [BP+10],AXPOP BPRET 4CHECK ENDPEND START修改符号后2.用DEBUG的R命令,T命令或G命令和D命令检查远程调用及近程调用时堆栈的变化。
1、子程序的调用和返回指令子程序的调用和返回是一对互逆操作,也是一种特殊的转移操作。
一方面,之所以说是转移,是因为当调用一个子程序时,程序的执行顺序被改变,CPU将转而执行子程序中的指令序列,在这方面,调用子程序的操作含有转移指令的功能,子程序的返回指令的转移特性与此类似;另一方面,转移指令是一种“一去不复返”的操作,而当子程序完后,还要求CPU能转而执行调用指令之下的指令,它是一种“有去有回”的操作。
为了满足子程序调用和返回操作的特殊性,在指令系统中设置了相应的特定指令。
1、1调用指令(CALL)调用子程序指令的格式如下:CALL 子程序名/Reg/Mem子程序的调用指令分为近(near)调用和远(far)调用。
如果被调用子程序的属性是近的,那么,CALL指令将产生一个近调用,它把该指令之后地址的偏移量(用一个字来表示的)压栈,把被调用子程序入口地址的偏移量送给指令指针寄存器IP即可实现执行程序的转移如果被调用子程序的属性是远的,那么,CALL指令将产生一个远调用。
这时,调用指令不仅要把该指令之后地址的偏移量压进栈,而且也要把段寄存器CS的值压进栈。
在此之后,再把被调用子程序入口地址的偏移量和段值分别送给IP和CS,这样完成了子程序的远调用操作00405600 call 0040689500405604 ......子程序调用指令本身的执行不影响任何标志位,但子程序体中指令的执行会改变标志位,所以,如果希望子程序的执行不能改变调用指令前后的标志位,那么,就要在子程序的开始处保护标志位,在子程序的返回前恢复标志位。
例如:CALL DISPLAY;DISPLAY是子程序名CALL BX;BX的内容是子程序的偏移量CALL WORD1;WORD1是内存字变量,其值是子程序的偏移量CALL DWORD1;DWORD1是双字变量,其值是子程序的偏移量和段值CALL word ptr [BX];BX所指内存字单元的值是子程序的偏移量CALL dword ptr [BX];BX所指内存双字单元的值是子程序的偏移量和段值1、2返回指令(RET)当子程序执行完时,需要返回到调用它的程序之中。
子程序调用指令
子程序调用指令是指一个程序在一段程序运行时,该段程序执行某个功能时,会调用由另一段子程序处理的指令的一种机器指令。
子程序调用指令的运行过程主要分为六个步骤:
1、先将书写代码时在子程序指令里声明的一个标记符(参数可以有一到多个)放入程序计数器,编程使程序跳转至对应的子程序。
2、将参数放入堆栈中。
3、程序计数器指向下一个指令(位置),将其存入返回地址寄存器中。
4、转至子程序,调用子程序中的指令,直至子程序返回跳转指令。
5、将从子程序返回的参数或结果放入程序总线中。
6、程序计数器指向堆栈中保存的返回地址,继续执行程序。
子程序调用指令的使用有很多优点,可以将程序设计的复杂性降低,提高代码的可维护性,减少重复的编码,并使程序更加结构良好和清晰。
同时,由于调用子程序时需要使用到跳转指令,也可以减少程序运行所需的时间。
然而,程序的运行效率有时也会根据子程序的大小和复杂性以及条件的多少而有所降低。
c语言调用子程序C语言是一种广泛应用于计算机编程的编程语言。
它具有高效、灵活的特点,可以用于开发各种类型的应用程序。
在C语言中,调用子程序是一种常见的编程技巧,可以将程序的功能模块化,提高代码的可读性和重用性。
下面我将通过一个例子来说明如何在C语言中调用子程序。
假设我们要编写一个程序,实现一个简单的计算器功能,可以进行加法、减法、乘法和除法运算。
首先,我们可以定义一个名为"calculator"的子程序,用于接收用户输入的两个操作数和运算符,并根据运算符的不同执行相应的计算操作。
在C语言中,可以使用函数来定义子程序。
下面是一个示例代码:```c#include <stdio.h>float calculator(float num1, float num2, char operator) {float result;switch (operator) {case '+':result = num1 + num2;break;case '-':result = num1 - num2;break;case '*':result = num1 * num2;break;case '/':result = num1 / num2;break;default:printf("Invalid operator!\n");result = 0;break;}return result;}int main() {float num1, num2;char operator;printf("Please enter the first number: "); scanf("%f", &num1);printf("Please enter the second number: ");scanf("%f", &num2);printf("Please enter the operator (+, -, *, /): ");scanf(" %c", &operator);float result = calculator(num1, num2, operator);printf("The result is: %.2f\n", result);return 0;}```在上面的代码中,我们首先定义了一个名为"calculator"的子程序,它接收三个参数:两个操作数和一个运算符。
ARM子程序设计实验7.1实验目的①了解子程序编写及调用。
②掌握ARM乘法指令的使用方法7.2 实验环境①硬件:PC机②软件:ADS1.27.3 实验内容①MUL指令编写一个整数乘方的子程序。
②多寄存器传送指令(STMFD/LDMFD)运用和各种变址寻址模式的掌握。
③BL指令调用子程序计算X n7.4 实验过程(1)新建ARM工程exp7_1启动ADS开发环境,选择File→New(Project)选项,使用ARM Executable Image工程模板创建一个工程exp7_1.(2) 新建汇编程序文件exp7.s,并将其添加到工程exp7_1中选择File→New(File)选项,新建汇编源程序文件exp7.s并添加到工程exp7_1中,exp7.s 源程序的参考代码如下:X EQU 3n EQU 4AREA Lab1,CODE,READONL YENTRYSTART LDR SP,=0X30003F00LDR R0,=XLDR R1,=nBL POWhalt b haltPOW STMFD SP!,{R1-R12,LR}MOVS R2,R1MOVEQ R0,#1BEQ POW_ENDMOV R1,R0SUB R2,R2,#1POW_L1 BL DO_MULSUBS R2,R2,#1BNE POW_L1POW_END LDMFD SP!,{R1-R12,PC}DO_MUL MUL R0,R1,R0MOV PC,LREND(3) 设置工程exp7的编译和链接选项设置工程链接地址RO Base:0x3000000,RW Base:0x3003000, 设置调试地址:0x3000000(4) 编译和链接工程在工程exp7窗口中,选择Make工具按钮,编译和链接工程exp6_1,如果有错误提示,请检查修改程序中的语法错误,直到编译和链接通过。
(5)在AXD中加载映像文件打开AXD Debugger,首先确认调试目标机是否设置为Armulator。
PLC案例详解子程序调用今天我们以三菱PLC为例,学习一下什么是子程序。
一套程序可以分为主程序区,还有子程序区,中断程序区等等,主程序区的程序是无时无刻都会扫描执行的,而子程序还有中断程序就必须触发某些条件,子程序才会执行。
那么问题来了,为什么不在主程序里面,加一个常开触点,触点闭合时就执行相应的程序段,这样不是也能够达到一样的效果么?为什么要出现子程序这样的东西呢?那是由于PLC的扫描周期引起的,PLC运行的速度,也就是扫描周期,受到I/O数量、通讯、还有程序的大小限制。
单独谈论程序大小,扫描的程序越多,扫描周期越长。
下面我们拿三菱FX3U-16MT 这款plc做了测试。
如上图,监控PLC的2个特殊寄存器,D8011是扫描程序的最短时间,D8012是扫描程序的最长时间,底下是程序的步数,没有写程序时,扫描周期平均需要5ms,随着程序增加,扫描周期不断变长;到16000步时,平均的扫描周期达到了25ms。
由此可见减少扫描程序的必要性,下面我们来看一下,子程序的具体使用方法。
这段程序分为主程序区和P0子程序两个部分,从程序第一行开始到FEND指令,这些都是主程序,FEND是主程序的结束标志。
左边的P0编号到SRET子程序结束指令,这是每一段完整的子程序必有的内容。
该程序中,当M2常开触点触发CALL P0指令时,P0子程序区的程序才能够执行,这时可以触发M1常开触点,Y4线圈会导通;这时有一个需要非常注意的点,在触发Y4的时候,断开M2,也就是停下P0子程序的运行,Y4会保持输出的状态,就算这时M1断开了,Y4也保持输出。
除非再次触发P0子程序检测到M1断开了,Y4才会断开。
子程序的调用除了普通的用法,还可以多层嵌套,如下图:通过X001调用子程序P11,运行P11时,通过触发X2按钮,调用P12子程序。
这样的写法最多支持5层的嵌套。
那么,子程序一般用在哪里呢?比如下面一条传送带,有很多个工艺,假如其中有一些工艺有些人需要,有一些人不需要,那么,就可以采用子程序的用法,在需要时我们就调用相应的工艺程序,不需要时,就不调用比如下面的一个分检机构,通过前面的感应器区分大中小部件,然后把部件放到相应的箱子里面,这时候我们也可以使用子程序调用,分别把大中小部件分别写一段子程序。
实验三编程实现子程序调用
一编程实现:
1.主程序由键盘输入一个数(<10),用子程序1实现DATA段以这个数为首连续十个整数填充字节数组buf。
譬如显示提示:”Please input a number:”,键盘输入3,执行子程序1后buf db 3,4,5,6,7,8,9,10,11,12。
2.主程序实现十个数求和,调用另一子程序2实现将和逐位转为ASCII码显示:”Sum is: xx”。
提示:和不会超过十进制3位数,所以将和除以10,商即为十位数,余数即为个位数,再加上30H即分别可以得它们的ASCII码。
二流程图
三实验程序:
data segment
buf db 10 dup(?)
str1 db 0ah,0dh,'Please input a number(<10):','$' str2 db 0ah,0dh,'Sum is ','$'
str3 db 0ah,0dh,'Input data error!','$';错误提示信息data ends
stac segment
stal db 100 dup(?);堆栈初始化
stac ends
code segment
assume ss:stac,cs:code,ds:data
begin:mov ax,stac
mov ss,ax
mov ax,data
mov ds,ax
kaishi:mov dx,offset str1
mov ah,9
int 21h
xor ax,ax
mov ah,1
int 21h
cmp al,'0'
jb msg
ja msg
mov si,offset buf
xor cx,cx
call xcbh;数组填充
xor ax,ax
mov cl,0ah
mov si,0;地址初始化
jia: add al,[si];数组逐个数相加
inc si;地址加1
loop jia
xor bx,bx
mov bl,10;高位输出10位数,低位输出余数
div bl
xor dx,dx
push ax
mov dx,offset str2
mov ah,9
int 21h
pop ax
add al,30h
mov dl,al
mov ah,2
int 21h
pop ax
xor dx,dx
add ah,30h
mov dl,ah
mov ah,2
int 21h
jmp jieshu xcbh proc
push cx
push si
push ax
sub al,30h
mov cx,0ah again:mov [si],al
inc si
inc al
loop again
pop ax
pop si
ret
xcbh endp
msg: mov dx,offset str3
mov ah,9
int 21h
jmp begin
jieshu:mov ah,4ch;调用4c退出程序功能int 21h
code ends
end begin
四:实验截图:
五.实验结论:
通过这个实验,我知道了在一个程序中有多个子程序。
为了完成一个指令需要多个子程序的调用。
这些子程序就组合成程序的机构。
希望再接再厉。