子程序設(shè)計

發(fā)布時間:2024-02-16
子程序是由設(shè)計者定義的完成某種功能的程序模塊。一旦定義了,該子程序可被任意調(diào)用。

sendat proc far ;子程序定義偽指令語句
push ax ;保護 ax 、 dx 、 si 寄存器
push dx
push si
lea si , bufr ;子程序待輸出的數(shù)據(jù)的首地址
goon : mov dx , 03fbh
wait : in al , dx ;讀端口 03fbh 讀入數(shù)據(jù)
test al , 20h
jz wait
mov al , [si] ;將緩沖區(qū) bufr 按字節(jié)裝入
mov dx , 03f 8h
out dx , al ;輸出至端口 03f 8h
inc si
cmp al , 0ah ;判斷輸出數(shù)據(jù)是否為結(jié)束
jne goon ;不為 0ah 則轉(zhuǎn)至 goon
pop si ;恢復(fù)寄存器
pop dx
pop ax
ret
sendat endp
上面的子程序,可以把數(shù)據(jù)段 bufr 緩沖區(qū),以 oa 結(jié)束的數(shù)據(jù),輸出到 03f 8h 端口。
主程序在調(diào)用子程序時,一方面初始數(shù)據(jù)要傳遞給子程序,另一方面子程序運行的結(jié)果要傳遞給主程序。盡管沒有初始數(shù)據(jù)或沒有運行結(jié)果的情況也有,但一般情況下是必須考慮的。 在編寫較為復(fù)雜的子程序時,可能出現(xiàn)子程序中調(diào)用子程序的情況,通常稱這種情況叫子程序嵌套。子程序嵌套層次的深度受堆棧大小的影響,由于堆棧不僅在子程序中使用,還受多方面影響,必須保證整個程序運行過程中,堆棧不能溢出。
把功能相對獨立的程序段單獨編寫和調(diào)試,作為一個相對獨立的模塊供程序使用,就形成子程序子程序可以實現(xiàn)源程序的模塊化,可簡化源程序結(jié)構(gòu),可以提高編程效率
1、程序定義偽指令
格式 : 過程名 proc [near|far]
. 過程名 endp
過程名(子程序名)為符合語法的標識符
near 屬性(段內(nèi)近調(diào)用)的過程只能被相同代碼段的其他程序調(diào)用
far 屬性(段間遠調(diào)用)的過程可以被相同或不同代碼段的程序調(diào)用
對簡化段定義格式,在微型、小型和緊湊存儲模式下,過程的缺省屬性為 near ;在中型、大型和巨型存儲模式下,過程的缺省屬性為 far
對完整段定義格式,過程的缺省屬性為 near
用戶可以在過程定義時用 near 或 far 改變?nèi)笔傩?br>子程序常見格式:
subname proc ; 具有缺省屬性的 subname 過程
push ax ; 保護寄存器:順序壓入堆棧
push bx ;ax/bx/cx 僅是示例
push cx
… ; 過程體 , 程序的主要功能
pop cx ; 恢復(fù)寄存器:逆序彈出堆棧
pop bx
pop ax
ret ; 過程返回
subname endp ; 過程結(jié)束
; 子程序功能:實現(xiàn)光標回車換行
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 ; 恢復(fù)寄存器 dx 和 ax
pop ax
ret ; 子程序返回
dpcrlf endp ; 過程結(jié)束
例 : 無參數(shù)傳遞的子程序
aldisp proc ; 實現(xiàn) al 內(nèi)容的顯示
push ax ; 過程中使用了 ax 、 cx 和 dx
push cx
push dx
push ax ; 暫存 ax
mov dl,al ; 轉(zhuǎn)換 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
例 : 實現(xiàn) al 內(nèi)容顯示的子程序
pop dx ; 恢復(fù)原 ax 值到 dx
and dl,0fh ; 轉(zhuǎn)換 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; 調(diào)用程序段開始
mov cx,count
displp: mov al,[bx]
call aldisp ; 調(diào)用顯示過程
mov dl,',' ; 顯示一個逗號,分隔數(shù)據(jù)
mov ah,2
int 21h
inc bx
loop displp ; 調(diào)用程序段結(jié)束
.exit 0
... ; 過程定義
end
htoasc proc
; 將 al 低 4 位表達的一位 16 進制數(shù)轉(zhuǎn)換為 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、子程序的參數(shù)傳遞
入口參數(shù)(輸入?yún)?shù)):主程序提供給子程序
出口參數(shù)(輸出參數(shù)):子程序返回給主程序
參數(shù)的形式:
① 數(shù)據(jù)本身(傳值)
② 數(shù)據(jù)的地址(傳址)
傳遞的方法:
① 寄存器 ② 變量 ③ 堆棧
例:求校驗和
子程序計算數(shù)組元素的“校驗和”
校驗和是指不記進位的累加
入口參數(shù): 數(shù)組的邏輯地址(傳址)
元素個數(shù)(傳值)
出口參數(shù): 求和結(jié)果(傳值)
把參數(shù)存于約定的寄存器中,可以傳值,也可以傳址。
子程序?qū)в谐隹趨?shù)的寄存器不能保護和恢復(fù)(主程序視具體情況進行保護)
子程序?qū)в腥肟趨?shù)的寄存器可以保護,也可以不保護;但最好一致
例 :
入口參數(shù): cx =元素個數(shù),
ds:bx =數(shù)組的段地址:偏移地址
出口參數(shù): al =校驗和
用寄存器傳遞參數(shù)
.startup
; 設(shè)置入口參數(shù)(含有 ds ←數(shù)組的段地址)
mov bx,offset array
;bx ←數(shù)組的偏移地址
mov cx,count ;cx ←數(shù)組的元素個數(shù)
call checksuma ; 調(diào)用求和過程
mov result,al ; 處理出口參數(shù)
.exit 0
checksuma proc
xor al,al ; 累加器清 0
suma: add al,[bx] ; 求和
inc bx ; 指向下一個字節(jié)
loop suma
ret
checksuma endp
end
主程序和子程序直接采用同一個變量名共享同一個變量,實現(xiàn)參數(shù)的傳遞
不同模塊間共享時,需要聲明
例 :
入口參數(shù):
count =元素個數(shù),
array =數(shù)組名(含段地址:偏移地址)
出口參數(shù):
result =校驗和
用變量傳遞參數(shù)
; 主程序
call checksumb
; 子程序
checksumb proc
push ax
push bx
push cx
xor al,al ; 累加器清 0
mov bx,offset array
;bx ←數(shù)組的偏移地址
mov cx,count
;cx ←數(shù)組的元素個數(shù)
sumb: add al,[bx] ; 求和
inc bx
loop sumb
mov result,al ; 保存校驗和
pop cx
pop bx
pop ax
ret
checksumb endp
主程序?qū)⒆映绦虻娜肟趨?shù)壓入堆棧,子程序從堆棧中取出參數(shù)
子程序?qū)⒊隹趨?shù)壓入堆棧,主程序彈出堆棧取得它們
例 :
入口參數(shù):
順序壓入偏移地址和元素個數(shù)
出口參數(shù):
al =校驗和
用堆棧傳遞參數(shù)
.startup
mov ax,offset array
push ax
mov ax,count
push ax
call checksumc
add sp,4
mov result,al
.exit 0
要注意堆棧的分配情況,保證參數(shù)存取正確、子程序正確返回,并保持堆棧平衡
checksumc proc
push bp
mov bp,sp ; 利用 bp 間接尋址存取參數(shù)
push bx
push cx
mov bx,[bp+6] ;ss:[bp+6] 指向偏移地址
mov cx,[bp+4] ;ss:[bp+4] 指向元素個數(shù)
xor al,al
sumc: add al,[bx]
inc bx
loop sumc
pop cx
pop bx
pop bp
ret
checksumc endp
堆棧區(qū)及參數(shù)
主程序?qū)崿F(xiàn)平衡堆棧: add sp,n
子程序?qū)崿F(xiàn)平衡堆棧: ret n
子程序的嵌套
1 .子程序的嵌套
2 .嵌套深度。
例:設(shè)從 buf 開始存放若干無符號字節(jié)數(shù)據(jù),找出其中的最小值并以 16 進制形式輸出。
子程序內(nèi)包含有子程序的調(diào)用就是子程序嵌套沒有什么特殊要求
aldisp proc
push ax
push cx ; 實現(xiàn) al 內(nèi)容的顯示
push ax ; 暫存 ax
mov cl,4
shr al,cl ; 轉(zhuǎn)換 al 的高 4 位
call htoasc ; 子程序調(diào)用(嵌套)
pop ax ; 轉(zhuǎn)換 al 的低 4 位
call htoasc ; 子程序調(diào)用(嵌套)
pop cx
pop ax
ret
aldisp endp
例 : 嵌套子程序
; 將 al 低 4 位表達的一位 16 進制數(shù)轉(zhuǎn)換為 ascii 碼
htoasc proc
push ax
push bx
push dx
mov bx,offset ascii;bx 指向 ascii 碼表
and al,0fh ; 取得一位 16 進制數(shù)
xlat cs:ascii
; 換碼: al ← cs:[bx + al] ,注意數(shù)據(jù)在代碼段 cs
mov dl,al ; 顯示
mov ah,2
int 21h
pop dx
pop bx
pop ax
ret ; 子程序返回
; 子程序的數(shù)據(jù)區(qū)
ascii db 30h,31h,32h,33h,34h,35h,36h,37h
db 38h,39h,41h,42h,43h,44h,45h,46h
htoasc endp
這是一個具有局部變量的子程序。因為數(shù)據(jù)區(qū)與子程序都在代碼段,所以利用了換碼指令 xlat 的另一種助記格式(寫出指向緩沖區(qū)的變量名,目的是便于指明段超越前綴)。串操作 movs 、 lods 和 cmps 指令也可以這樣使用,以便使用段超越前綴
除采用段超越方法外,子程序與主程序的數(shù)據(jù)段不同時,我們還可以通過修改 ds 值實現(xiàn)數(shù)據(jù)存??;但需要保護和恢復(fù) ds 寄存器
子程序的遞歸
當子程序直接或間接地嵌套調(diào)用自身時稱為遞歸調(diào)用,含有遞歸調(diào)用的子程序稱為遞歸子程序
遞歸子程序必須采用寄存器或堆棧傳遞參數(shù),遞歸深度受堆??臻g的限制
例:求階乘
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 ; 入口參數(shù): n
call fact ; 調(diào)用遞歸子程序
pop result ; 出口參數(shù): n !
.exit 0
例 : 求自然數(shù) n(n>=1) 的階乘
; 計算 n! 的近過程
; 入口參數(shù):壓入 n ; 出口參數(shù):彈出 n!
fact proc
push ax
push bp
mov bp,sp
mov ax,[bp+6] ; 取入口參數(shù) 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 ; 調(diào)用遞歸子程序求 (n-1)!
pop ax
mul word ptr [bp+6] ; 求 n × (n-1)!
fact2: mov [bp+6],ax ; 存入出口參數(shù) n!
pop bp
pop ax
ret
fact endp
調(diào)用時進棧
返回時出棧
1
3!
2!
1!
子程序的重入
可重入子程序是指該子程序被某程序調(diào)用 , 但還未結(jié)束 , 又被另一個程序調(diào)用 , 這是在分時系統(tǒng)中 .
子程序的重入是指子程序被中斷后又被中斷服務(wù)程序所調(diào)用,能夠重入的子程序稱為可重入子程序。在子程序中,注意利用寄存器和堆棧傳遞參數(shù)和存放臨時數(shù)據(jù),而不要使用固定的存儲單元(變量),就能夠?qū)崿F(xiàn)重入。
子程序的重入不同于子程序的遞歸。重入是被動地進入,而遞歸是主動地進入;重入的調(diào)用間往往沒有關(guān)系,而遞歸的調(diào)用間卻是密切相關(guān)的。遞歸子程序也是可重入子程序。
ascii 碼轉(zhuǎn)換為二進制數(shù)
① 首先判斷輸入為正或負數(shù),并用一個寄存器記錄
② 接著輸入 0 ~ 9 數(shù)字( ascii 碼),并減 30h 轉(zhuǎn)換為二進制數(shù)
③ 然后將前面輸入的數(shù)值乘 10 ,并與剛輸入的數(shù)字相加得到新的數(shù)值
④ 重復(fù)②、③步,直到輸入一個非數(shù)字字符結(jié)束
⑤ 負數(shù)進行求補,轉(zhuǎn)換成補碼;否則直接保存數(shù)值
本例采用 16 位寄存器表達數(shù)據(jù),所以只能輸入+ 327677 ~- 32768 間的數(shù)值
但該算法適合更大范圍的數(shù)據(jù)
例題 :
例:從鍵盤輸入有符號十進制數(shù)
子程序從鍵盤輸入一個有符號十進制數(shù);子程序還包含將 ascii 碼轉(zhuǎn)換為二進制數(shù)的過程
輸入時,負數(shù)用“-”引導(dǎo),正數(shù)直接輸入或用“+”引導(dǎo)
子程序用寄存器傳遞出口參數(shù),主程序調(diào)用該子程序輸入 10 個數(shù)據(jù)
.data
count = 10
array dw count dup(0) ; 預(yù)留數(shù)據(jù)存儲空間
.code
.startup
mov cx,count
mov bx,offset array
again: call read ; 調(diào)用子程序輸入一個數(shù)據(jù)
mov [bx],ax ; 將出口參數(shù)存放緩沖區(qū)
inc bx
inc bx
call dpcrlf
; 調(diào)用子程序,光標回車換行以便輸入下一個數(shù)據(jù)
loop again
.exit 0
; 輸入有符號 10 進制數(shù)的通用子程序
; 出口參數(shù): ax =補碼表示的二進制數(shù)值
; 說明:負數(shù)用“-”引導(dǎo),正數(shù)用“+”引導(dǎo)或直接輸入;數(shù)據(jù)范圍是+ 32767 ~- 32768
read proc
push bx
push cx
push dx
xor bx,bx ;bx 保存結(jié)果
xor cx,cx
;cx 為正負標志, 0 為正,- 1 為負
mov ah,1 ; 輸入一個字符
int 21h
cmp al,'+' ; 是“+”,繼續(xù)輸入字符
jz read1
cmp al,'-' ; 是“-”,設(shè)置- 1 標志
jnz read2 ; 非“+”和“-”,轉(zhuǎn) read2
mov cx,-1
read1: mov ah,1 ; 繼續(xù)輸入字符
int 21h
read2: cmp al,'0‘
; 不是 0 ~ 9 之間的字符,則輸入數(shù)據(jù)結(jié)束
jb read3
cmp al,'9'
ja read3
sub al,30h
; 是 0 ~ 9 之間的字符,則轉(zhuǎn)換為二進制數(shù)
; 利用移位指令,實現(xiàn)數(shù)值乘 10 : bx ← bx × 10
shl bx,1
mov dx,bx
shl bx,1
shl bx,1
add bx,dx
;bx 內(nèi)容乘 10
mov ah,0
add bx,ax
; 已輸入數(shù)值乘 10 后,與新輸入數(shù)值相加
jmp read1 ; 繼續(xù)輸入字符
read3: cmp cx,0
jz read4
neg bx ; 是負數(shù),進行求補
例:顯示有符號十進制數(shù)
子程序在屏幕上顯示一個有符號十進制數(shù);子程序還包含將二進制數(shù)轉(zhuǎn)換為 ascii 碼的過程
顯示時,負數(shù)用“-”引導(dǎo),正數(shù)直接輸出、沒有前導(dǎo)字符
子程序的入口參數(shù)用共享變量傳遞,主程序調(diào)用該子程序顯示 10 個數(shù)據(jù)
.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 ; 將入口參數(shù)存入共享變量
call write ; 調(diào)用子程序顯示一個數(shù)據(jù)
inc bx
inc bx
call dpcrlf ; 便于顯示下一個數(shù)據(jù)
loop again
.exit 0
; 顯示有符號 10 進制數(shù)的通用子程序
入口參數(shù):共享變量 wtemp
write proc
push ax
push bx
push dx
mov ax,wtemp ; 取出顯示數(shù)據(jù)
test ax,ax ; 判斷零、正數(shù)或負數(shù)
jnz write1
mov dl,'0' ; 是零,顯示“ 0 ” 后退出
mov ah,2
int 21h
jmp write5
write1: jns write2 ; 是負數(shù),顯示“-”
mov bx,ax ;ax 數(shù)據(jù)暫存于 bx
mov dl,'-'
mov ah,2
int 21h
mov ax,bx
neg ax ; 數(shù)據(jù)求補(求絕對值)
write2: mov bx,10
push bx
;10 壓入堆棧,作為退出標志
write3: cmp ax,0 ; 數(shù)據(jù)(余數(shù))為零
jz write4 ; 轉(zhuǎn)向顯示
sub dx,dx ; 擴展被除數(shù) dx.ax
div bx ; 數(shù)據(jù)除以 10 : dx.ax ÷ 10
add dl,30h
; 余數(shù)( 0 ~ 9 )轉(zhuǎn)換為 ascii 碼
push dx
; 數(shù)據(jù)各位先低位后高位壓入堆棧
jmp write3
write4: pop dx
; 數(shù)據(jù)各位先高位后低位彈出堆棧
cmp dl,10 ; 是結(jié)束標志 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
上一個:銀杏樹移栽技術(shù)要點
下一個:CR0402FF2800G 麗智電阻0402 280Ω ±1% 1/16W

win11 內(nèi)存(windows11內(nèi)存完整性)
聯(lián)想一直顯示正在準備自動修復(fù),聯(lián)想電腦顯示正在準備windows
平安保險公司有企業(yè)郵箱嗎
花卉發(fā)生藥害的癥狀及其原因
子女的撫養(yǎng)費標準及相關(guān)法律規(guī)定
RC1210FR-0732K4L,1210 32.4KΩ 1% 1/2W 電阻
RTA03-4D6201FTP排阻,旺詮RF4D03-6.2K
中聯(lián)教您正確使用保溫齒輪泵和故障的處理
收藏普洱茶是提入還是件入?只有資深藏家才知道的秘密
泰坦軍團顯示器評測(泰坦軍團顯示器是什么牌子的)
十八禁 网站在线观看免费视频_2020av天堂网_一 级 黄 色 片免费网站_绝顶高潮合集Videos