安防监控知识
电气知识|子程序设计
2016-03-26  浏览:59
安防之家讯:子程序是由设计者定义的完成某种功能的程序模块。一旦定义了,该子程序可被任意调用。





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评
  • 评论登陆
  • 移动社区 天花之家 木门之家 灯具之家 铁艺之家 幕墙之家 五金头条 楼梯头条 墙纸头条 壁纸头条 玻璃头条 老姚之家 灯饰之家 电气之家 全景头条 照明之家 防水之家 防盗之家 区快洞察 建材 枣庄建材 临沂建材 南昌建材 上饶建材 抚州建材 宜春建材 吉安建材 赣州建材 鹰潭建材 新余建材 九江建材 萍乡建材 景德镇陶瓷 石家庄建材 衡水建材 廊坊建材 沧州建材 承德建材 建材之家 企业之家 720全景
    (c)2015-2017 Bybc.cn SYSTEM All Rights Reserved