电气知识|子程序设计

   更新日期:2017-03-26     来源:建材之家    作者:安防之家    浏览:41    评论:0    
核心提示:子程序是由设计者定义的完成某种功能的程序模块。一旦定义了,该子程序可被任意调用。例SENDAT PROC FAR ;子程序定义伪指令语句 PUSH AX ;保护 AX 、 DX 、 SI 寄存器 PUSH DX PUSH SI LEA SI , BUFR ;子程序待输出的数据的首地址 GOON : MOV DX , 03FBH WAIT : IN AL , DX ;读端口 03FBH 读入

安防知识|防护网分类与介绍

推荐简介:安防之家讯:防护网,是一种安装于窗户、阳台等处,为居家生活提供防护、防盗、防坠物等安全保障的新型建筑安防产品,它集安全、美观、实用等诸多优点于一身,代表着21世纪家居安防的最新理念。防护网分类防护网分为:山坡防护网(边坡防护网,主动防护网,柔性防护网,被动防护网)(公路、铁路)防护网,园林防护网、体育场防护网、居民区防护网等......
安防之家讯:子程序是由设计者定义的完成某种功能的程序模块。一旦定义了,该子程序可被任意调用。





SENDAT PROC FAR ;子程序定义伪指令语句



PUSH AX ;保护 AX 、 DX 、 SI 寄存器



PUSH DX



PUSH SI



LEA SI , BUFR ;子程序待输出的数据的首地址



GOON : MOV DX , 03FBH



WAIT : IN AL , DX ;读端口 03FBH 读入数据



TEST AL , 20H



JZ WAIT



MOV AL , [SI] ;将缓冲区 BUFR 按字节装入



MOV DX , 03F 8H



OUT DX , AL ;输出至端口 03F 8H



INC SI



CMP AL , 0AH ;判断输出数据是否为结束



JNE GOON ;不为 0AH 则转至 GOON



POP SI ;恢复寄存器



POP DX



POP AX



RET



SENDAT ENDP

上面的子程序,可以把数据段 BUFR 缓冲区,以 OA 结束的数据,输出到 03F 8H 端口。

主程序在调用子程序时,一方面初始数据要传递给子程序,另一方面子程序运行的结果要传递给主程序。尽管没有初始数据或没有运行结果的情况也有,但一般情况下是必须考虑的。 在编写较为复杂的子程序时,可能出现子程序中调用子程序的情况,通常称这种情况叫子程序嵌套。子程序嵌套层次的深度受堆栈大小的影响,由于堆栈不仅在子程序中使用,还受多方面影响,必须保证整个程序运行过程中,堆栈不能溢出。

把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成子程序子程序可以实现源程序的模块化,可简化源程序结构,可以提高编程效率

1、程序定义伪指令

格式 : 过程名 proc [near|far]

. 过程名 endp

过程名(子程序名)为符合语法的标识符

NEAR 属性(段内近调用)的过程只能被相同代码段的其他程序调用

FAR 属性(段间远调用)的过程可以被相同或不同代码段的程序调用

对简化段定义格式,在微型、小型和紧凑存储模式下,过程的缺省属性为 near ;在中型、大型和巨型存储模式下,过程的缺省属性为 far

对完整段定义格式,过程的缺省属性为 near

用户可以在过程定义时用 near 或 far 改变缺省属性



子程序常见格式:

subname proc ; 具有缺省属性的 subname 过程

push ax ; 保护寄存器:顺序压入堆栈

push bx ;ax/bx/cx 仅是示例

push cx

… ; 过程体 , 程序的主要功能

pop cx ; 恢复寄存器:逆序弹出堆栈

pop bx

pop ax

ret ; 过程返回

subname endp ; 过程结束

; 子程序功能:实现光标回车换行

dpcrlf proc ; 过程开始

push ax ; 保护寄存器 AX 和 DX

push dx

mov dl,0dh ; 显示回车

mov ah,2

int 21h

mov dl,0ah ; 显示换行

mov ah,2

int 21h

pop dx ; 恢复寄存器 DX 和 AX

pop ax

ret ; 子程序返回

dpcrlf endp ; 过程结束

例 : 无参数传递的子程序

ALdisp proc ; 实现 al 内容的显示

push ax ; 过程中使用了 AX 、 CX 和 DX

push cx

push dx

push ax ; 暂存 ax

mov dl,al ; 转换 al 的高 4 位

mov cl,4

shr dl,cl

or dl,30h ;al 高 4 位变成 3

cmp dl,39h

jbe aldisp1

add dl,7 ; 是 0Ah ~ 0Fh ,还要加上 7

aldisp1: mov ah,2 ; 显示

int 21h

例 : 实现 AL 内容显示的子程序

pop dx ; 恢复原 ax 值到 dx

and dl,0fh ; 转换 al 的低 4 位

or dl,30h

cmp dl,39h

jbe aldisp2

add dl,7

aldisp2: mov ah,2 ; 显示

int 21h

pop dx

pop cx

pop ax

ret ; 过程返回

ALdisp endp

... ; 主程序

mov bx,offset array; 调用程序段开始

mov cx,count

displp: mov al,[bx]

call ALdisp ; 调用显示过程

mov dl,',' ; 显示一个逗号,分隔数据

mov ah,2

int 21h

inc bx

loop displp ; 调用程序段结束

.exit 0

... ; 过程定义

end

HTOASC proc

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

and al,0fh

cmp al,9

jbe htoasc1

add al,37h ; 是 0AH ~ 0FH ,加 37H

ret ; 子程序返回

htoasc1: add al,30h ; 是 0 ~ 9 ,加 30H

ret ; 子程序返回

HTOASC endp

2、子程序的参数传递

入口参数(输入参数):主程序提供给子程序

出口参数(输出参数):子程序返回给主程序

参数的形式:

① 数据本身(传值)

② 数据的地址(传址)

传递的方法:

① 寄存器 ② 变量 ③ 堆栈

例:求校验和

子程序计算数组元素的“校验和”

校验和是指不记进位的累加

入口参数: 数组的逻辑地址(传址)

元素个数(传值)

出口参数: 求和结果(传值)

把参数存于约定的寄存器中,可以传值,也可以传址。

子程序对带有出口参数的寄存器不能保护和恢复(主程序视具体情况进行保护)

子程序对带有入口参数的寄存器可以保护,也可以不保护;但最好一致

例 :

入口参数: CX =元素个数,

DS:BX =数组的段地址:偏移地址

出口参数: AL =校验和

用寄存器传递参数

.startup

; 设置入口参数(含有 DS ←数组的段地址)

mov bx,offset array

;BX ←数组的偏移地址

mov cx,count ;CX ←数组的元素个数

call checksuma ; 调用求和过程

mov result,al ; 处理出口参数

.exit 0

checksuma proc

xor al,al ; 累加器清 0

suma: add al,[bx] ; 求和

inc bx ; 指向下一个字节

loop suma

ret

checksuma endp

end

主程序和子程序直接采用同一个变量名共享同一个变量,实现参数的传递

不同模块间共享时,需要声明

例 :

入口参数:

count =元素个数,

array =数组名(含段地址:偏移地址)

出口参数:

result =校验和

用变量传递参数

; 主程序

call checksumb

; 子程序

checksumb proc

push ax

push bx

push cx

xor al,al ; 累加器清 0

mov bx,offset array

;BX ←数组的偏移地址

mov cx,count

;CX ←数组的元素个数

sumb: add al,[bx] ; 求和

inc bx

loop sumb

mov result,al ; 保存校验和

pop cx

pop bx

pop ax

ret

checksumb endp

主程序将子程序的入口参数压入堆栈,子程序从堆栈中取出参数

子程序将出口参数压入堆栈,主程序弹出堆栈取得它们

例 :

入口参数:

顺序压入偏移地址和元素个数

出口参数:

AL =校验和

用堆栈传递参数

.startup

mov ax,offset array

push ax

mov ax,count

push ax

call checksumc

add sp,4

mov result,al

.exit 0

要注意堆栈的分配情况,保证参数存取正确、子程序正确返回,并保持堆栈平衡

checksumc proc

push bp

mov bp,sp ; 利用 BP 间接寻址存取参数

push bx

push cx

mov bx,[bp+6] ;SS:[BP+6] 指向偏移地址

mov cx,[bp+4] ;SS:[BP+4] 指向元素个数

xor al,al

sumc: add al,[bx]

inc bx

loop sumc

pop cx

pop bx

pop bp

ret

checksumc endp

堆栈区及参数

主程序实现平衡堆栈: add sp,n

子程序实现平衡堆栈: ret n

子程序的嵌套

1 .子程序的嵌套

2 .嵌套深度。

例:设从 BUF 开始存放若干无符号字节数据,找出其中的最小值并以 16 进制形式输出。

子程序内包含有子程序的调用就是子程序嵌套没有什么特殊要求

ALdisp proc

push ax

push cx ; 实现 al 内容的显示

push ax ; 暂存 ax

mov cl,4

shr al,cl ; 转换 al 的高 4 位

call htoasc ; 子程序调用(嵌套)

pop ax ; 转换 al 的低 4 位

call htoasc ; 子程序调用(嵌套)

pop cx

pop ax

ret

ALdisp endp

例 : 嵌套子程序

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

HTOASC proc

push ax

push bx

push dx

mov bx,offset ASCII;BX 指向 ASCII 码表

and al,0fh ; 取得一位 16 进制数

xlat CS:ASCII

; 换码: AL ← CS:[BX + AL] ,注意数据在代码段 CS

mov dl,al ; 显示

mov ah,2

int 21h

pop dx

pop bx

pop ax

ret ; 子程序返回

; 子程序的数据区

ASCII db 30h,31h,32h,33h,34h,35h,36h,37h

db 38h,39h,41h,42h,43h,44h,45h,46h

HTOASC endp

这是一个具有局部变量的子程序。因为数据区与子程序都在代码段,所以利用了换码指令 XLAT 的另一种助记格式(写出指向缓冲区的变量名,目的是便于指明段超越前缀)。串操作 MOVS 、 LODS 和 CMPS 指令也可以这样使用,以便使用段超越前缀

除采用段超越方法外,子程序与主程序的数据段不同时,我们还可以通过修改 DS 值实现数据存取;但需要保护和恢复 DS 寄存器

子程序的递归

当子程序直接或间接地嵌套调用自身时称为递归调用,含有递归调用的子程序称为递归子程序

递归子程序必须采用寄存器或堆栈传递参数,递归深度受堆栈空间的限制

例:求阶乘

N!= N*(N-1)! 当 N>0

1 当 N=0

.model small

.stack 256

.data

N dw 3

result dw ?

.code

.startup

mov bx,N

push bx ; 入口参数: N

call fact ; 调用递归子程序

pop result ; 出口参数: N !

.exit 0

例 : 求自然数 N(N>=1) 的阶乘

; 计算 N! 的近过程

; 入口参数:压入 N ; 出口参数:弹出 N!

fact proc

push ax

push bp

mov bp,sp

mov ax,[bp+6] ; 取入口参数 N

cmp ax,0

jne fact1 ;N > 0,N! = N × (N-1)!

inc ax ;N = 0,N! = 1

jmp fact2

fact1: dec ax ;N-1

push ax

call fact ; 调用递归子程序求 (N-1)!

pop ax

mul word ptr [bp+6] ; 求 N × (N-1)!

fact2: mov [bp+6],ax ; 存入出口参数 N!

pop bp

pop ax

ret

fact endp

调用时进栈

返回时出栈

1

3!

2!

1!

子程序的重入

可重入子程序是指该子程序被某程序调用 , 但还未结束 , 又被另一个程序调用 , 这是在分时系统中 .

子程序的重入是指子程序被中断后又被中断服务程序所调用,能够重入的子程序称为可重入子程序。在子程序中,注意利用寄存器和堆栈传递参数和存放临时数据,而不要使用固定的存储单元(变量),就能够实现重入。

子程序的重入不同于子程序的递归。重入是被动地进入,而递归是主动地进入;重入的调用间往往没有关系,而递归的调用间却是密切相关的。递归子程序也是可重入子程序。

ASCII 码转换为二进制数

① 首先判断输入为正或负数,并用一个寄存器记录

② 接着输入 0 ~ 9 数字( ASCII 码),并减 30H 转换为二进制数

③ 然后将前面输入的数值乘 10 ,并与刚输入的数字相加得到新的数值

④ 重复②、③步,直到输入一个非数字字符结束

⑤ 负数进行求补,转换成补码;否则直接保存数值



本例采用 16 位寄存器表达数据,所以只能输入+ 327677 ~- 32768 间的数值

但该算法适合更大范围的数据

例题 :

例:从键盘输入有符号十进制数

子程序从键盘输入一个有符号十进制数;子程序还包含将 ASCII 码转换为二进制数的过程

输入时,负数用“-”引导,正数直接输入或用“+”引导

子程序用寄存器传递出口参数,主程序调用该子程序输入 10 个数据

.data

count = 10

array dw count dup(0) ; 预留数据存储空间

.code

.startup

mov cx,count

mov bx,offset array

again: call read ; 调用子程序输入一个数据

mov [bx],ax ; 将出口参数存放缓冲区

inc bx

inc bx

call dpcrlf

; 调用子程序,光标回车换行以便输入下一个数据

loop again

.exit 0

; 输入有符号 10 进制数的通用子程序

; 出口参数: AX =补码表示的二进制数值

; 说明:负数用“-”引导,正数用“+”引导或直接输入;数据范围是+ 32767 ~- 32768

read proc

push bx

push cx

push dx

xor bx,bx ;BX 保存结果

xor cx,cx

;CX 为正负标志, 0 为正,- 1 为负

mov ah,1 ; 输入一个字符

int 21h

cmp al,'+' ; 是“+”,继续输入字符

jz read1

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

jnz read2 ; 非“+”和“-”,转 read2

mov cx,-1

read1: mov ah,1 ; 继续输入字符

int 21h

read2: cmp al,'0‘

; 不是 0 ~ 9 之间的字符,则输入数据结束

jb read3

cmp al,'9'

ja read3

sub al,30h

; 是 0 ~ 9 之间的字符,则转换为二进制数

; 利用移位指令,实现数值乘 10 : BX ← BX × 10

shl bx,1

mov dx,bx

shl bx,1

shl bx,1

add bx,dx

;bx 内容乘 10

mov ah,0

add bx,ax

; 已输入数值乘 10 后,与新输入数值相加

jmp read1 ; 继续输入字符

read3: cmp cx,0

jz read4

neg bx ; 是负数,进行求补

例:显示有符号十进制数

子程序在屏幕上显示一个有符号十进制数;子程序还包含将二进制数转换为 ASCII 码的过程

显示时,负数用“-”引导,正数直接输出、没有前导字符





子程序的入口参数用共享变量传递,主程序调用该子程序显示 10 个数据

.data

count = 10

array dw 1234,-1234,0,1,-1,32767

dw -32768,5678,-5678,9000

wtemp dw ? ; 共享变量

.code

.startup

mov cx,count

mov bx,offset array

again: mov ax,[bx]

mov wtemp,ax ; 将入口参数存入共享变量

call write ; 调用子程序显示一个数据

inc bx

inc bx

call dpcrlf ; 便于显示下一个数据

loop again

.exit 0

; 显示有符号 10 进制数的通用子程序

入口参数:共享变量 wtemp

write proc

push ax

push bx

push dx

mov ax,wtemp ; 取出显示数据

test ax,ax ; 判断零、正数或负数

jnz write1

mov dl,'0' ; 是零,显示“ 0 ” 后退出

mov ah,2

int 21h

jmp write5

write1: jns write2 ; 是负数,显示“-”

mov bx,ax ;AX 数据暂存于 BX

mov dl,'-'

mov ah,2

int 21h

mov ax,bx

neg ax ; 数据求补(求绝对值)

write2: mov bx,10

push bx

;10 压入堆栈,作为退出标志

write3: cmp ax,0 ; 数据(余数)为零

jz write4 ; 转向显示

sub dx,dx ; 扩展被除数 DX.AX

div bx ; 数据除以 10 : DX.AX ÷ 10

add dl,30h

; 余数( 0 ~ 9 )转换为 ASCII 码

push dx

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

jmp write3

write4: pop dx

; 数据各位先高位后低位弹出堆栈

cmp dl,10 ; 是结束标志 10 ,则退出

je write5

mov ah,2 ; 进行显示

int 21h

jmp write4

write5: pop dx

pop bx

pop ax

ret ; 子程序返回

write endp

; 使光标回车换行的子程序

dpcrlf proc

... ; 省略

dpcrlf endp

end

安防之家专注于各种家居的安防,监控,防盗,安防监控,安防器材,安防设备的新闻资讯和O2O电商导购服务,敬请登陆安防之家:http://anfang.jc68.com/
小程序码
 
打赏
 
更多>文章标签:安防器材
更多>同类安防监控资讯
0相关评论

推荐图文更多...
点击排行更多...
安防监控商机更多...
安防监控圈更多...
最新视频更多...
推荐产品更多...
陶瓷头条 | 空调头条 | 卫浴头条 | 洁具头条 | 油漆头条 | 涂料头条 | 地板头条 | 吊顶头条 | 衣柜头条 | 家居头条 | 博一建材 | 博一建材 | 建材群站 | 建材资讯 | 建材商机 | 建材产品 | 水泥头条 | 楼梯之家 | 门窗之家 | 老姚之家 | 灯饰之家 | 电气之家 | 全景头条 | 照明之家 | 防水之家 | 防盗之家 | 区快洞察 | 漳州建材 | 泉州建材 | 三明建材 | 莆田建材 | 合肥建材 | 宣城建材 | 池州建材 | 亳州建材 | 六安建材 | 巢湖建材 | 宿州建材 | 阜阳建材 | 滁州建材 | 黄山建材 | 安庆建材 | 铜陵建材 | 淮北建材 | 马鞍山建材 |
建材 | 双碳之家 | 企业之家 | 移动社区 | 关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图 | 排名推广 | 广告服务 | 积分换礼 | RSS订阅 | sitemap | 粤ICP备14017808号
(c)2015-2017 Bybc.cn SYSTEM All Rights Reserved
Powered by 安防之家