摘? 要: 利用匯編模塊對C51模塊進行無參數(shù)化調(diào)用,從根本上避開了傳統(tǒng)匯編模塊和C51模塊之間調(diào)用時的繁瑣接口編程問題,并以實例驗證了該方法的優(yōu)越性和有效性。
關(guān)鍵詞: ASM51匯編語言? C51語言? 無參數(shù)化調(diào)用
?
MCS-51系列單片機在目前和今后的相當一段時間內(nèi)都將是我國的單片機主流機種。但在早期的開發(fā)過程中,程序員不得不從深奧的匯編語言開始摸索,同時要求開發(fā)人員對硬件亦有相當?shù)牧私?。相比而言,專?051系列單片機設(shè)計的Franklin C51語言是一種通用的高級結(jié)構(gòu)化的程序設(shè)計語言。入門容易,程序可讀性強,調(diào)試、移植都很方便,故開發(fā)效率高,尤其在數(shù)值運算處理方面具有很大的優(yōu)勢(這正是ASM51匯編語言的薄弱環(huán)節(jié))。不過,C語言雖然也可對計算機的硬件系統(tǒng)進行操作,但在處理特殊I/O口和中斷向量方面,不如匯編那樣直接、有效。因而,在效率為重的今天,將ASM51匯編與C51語言結(jié)合起來,充分發(fā)揮各自的優(yōu)勢,無疑是單片機開發(fā)人員的最佳選擇。
1 匯編與C51的混合編程
一般的做法都是利用C51上手容易、便于理解的優(yōu)勢來編寫主程序,在C51語言不便處理或者效率比較低時調(diào)用匯編函數(shù)??紤]到MCS-51(尤其是8031)內(nèi)部的資源配置情況:可用的RAM不到256字節(jié),5個固定地址的有限中斷源,4個8位并口中實際可作I/O口的只有P1口。因而要求開發(fā)者對單片機的內(nèi)部結(jié)構(gòu)有清楚的了解,并盡可能地統(tǒng)籌安排這些資源。事實也證明,不理解匯編語言是很難寫出高效程序的。故筆者的觀點是利用匯編語言對I/O接口、中斷向量及程序空間分配的巨大優(yōu)勢,讓程序員對MCS-51內(nèi)的每一個字節(jié)甚至是每一比特(可位尋址的空間)全部進行統(tǒng)籌安排,設(shè)計好各個程序模塊,包括I/O口地址和中斷向量地址的處理;同時在具體的數(shù)據(jù)處理、通信等不需要過多與硬件直接打交道的程序模塊中,充分利用C51語言強大高效的編程能力。
最后的關(guān)鍵是如何讓匯編模塊能夠正確識別C51函數(shù)并調(diào)用它來完成相應的功能。ASM51匯編與C51語言之間的調(diào)用約定并不簡單,而且各種編譯器使用的約定不盡相同,甚至還依賴于程序所選擇的大、中、小存儲模式。通常每個需傳遞的參數(shù)按調(diào)用順序和類型分別由約定的寄存器來傳遞。如果參數(shù)過多或者無足夠寄存器可用時,參數(shù)的傳遞將在固定的存儲器區(qū)域內(nèi)進行,相同類型的參數(shù)共享一個參數(shù)傳遞段,按參數(shù)調(diào)用順序遞增其存放地址,返回值也由約定的寄存器或地址段返回。由此可見程序調(diào)用的效率必將受到接口復雜度的影響。盡管目前的單片機仿真器已經(jīng)提供了標準接口的全自動轉(zhuǎn)換功能,減少了接口工作量,但在程序的調(diào)試及移植中,如果程序員不了解這些接口的各種約定,將對出現(xiàn)的錯誤不知所措。比如返回值不止一個時,編譯器自己就無法正確完成接口配置。這里力薦一種簡潔有效的調(diào)用方法——無參數(shù)化調(diào)用。
2 ASM51無參數(shù)化調(diào)用C51函數(shù)的實現(xiàn)原理
所謂的無參數(shù)化調(diào)用是指讓C51子函數(shù)不帶任何參數(shù),這樣就可以從根本上避開調(diào)用參數(shù)的傳遞和返回值的安排等繁瑣易出錯的問題,只需要簡單地在匯編語言開頭說明一下外部C51子函數(shù)(“EXTRN? code(
# define CBYTE((unsigned char *)0x50000L)
# define DBYTE((unsigned char *)0x50000L)
# define PBYTE((unsigned char *)0x50000L)
# define XBYTE((unsigned char *)0x50000L)
其中CBYTE定義為尋址CODE程序區(qū);DBYTE定義為尋址DATA數(shù)據(jù)區(qū);PBYTE定義為尋址相對于MOVX @R0″指令的分頁數(shù)據(jù)XDATA區(qū);XBYTE定義為尋址相對于MOVX @DPTR″指令的分頁數(shù)據(jù)XDATA區(qū)。它們的類型決定了絕對地址空間的位置。
引進該頭文件后,程序員就可以對8051系列單片機的存儲器進行絕對地址的訪問,把對參數(shù)值和返回值的操作轉(zhuǎn)化為對存儲器絕對地址的操作,像純匯編操作一樣,根本不用定義C51函數(shù)與匯編接口的參數(shù)和返回值的配置,從而提高了調(diào)用效率。具體做法是:先在C51函數(shù)中定義好傳遞參數(shù)和返回值所需要的各個絕對地址(視程序員自己的空間配置而定),在其它匯編模塊中將C51函數(shù)中將要使用的參數(shù)值放入這些絕對地址中,把被調(diào)用C51模塊中將輸出的計算值(可以不止一個)也放入類似的絕對地址中。于是,當C51函數(shù)中需要使用某個參數(shù)值時,就直接從相應的絕對地址中讀取該值;當別的匯編模塊中需要使用C51函數(shù)的返回值時,也直接對存放返回值的絕對地址進行讀操作即可。下面以一個調(diào)試通過的匯編調(diào)用C51函數(shù)的簡單程序為例進行具體說明。
3 ASM51無參數(shù)化調(diào)用C51函數(shù)的實現(xiàn)示例
該系統(tǒng)要求用單片機根據(jù)實時采樣輸入的轉(zhuǎn)速實現(xiàn)機車速度的測量,并可隨鍵盤輸入的車輪直徑變化實時調(diào)整車速,最后將車速和輪徑值都顯示出來。設(shè)計任務(wù)很簡單,編程中的最大難度就在于車速的計算程序。由于輪徑值要求精確到mm(最大值超過了1000),車速的計算結(jié)果要保留到小數(shù)點后一位,因此需要進行浮點數(shù)運算,期間還要完成數(shù)的各種進制間的換算。雖然算法簡單,但實際用匯編語言實現(xiàn)起來經(jīng)常考慮不周,調(diào)試起來費時費力(筆者調(diào)試通過的這段匯編代碼長達近400行)。這樣,自然就想到調(diào)用C51函數(shù)了,充分發(fā)揮兩種語言的優(yōu)勢。先用匯編語言設(shè)計好各個模塊,包括循環(huán)顯示車速和輪徑值的主程序模塊,響應采樣轉(zhuǎn)速值和鍵盤輸入兩個中斷模塊,代碼如下所示。
EXTRN CODE (CALL1)????????????????? ;聲明外部C51函數(shù)
ORG 0000H
LJMP MAIN
ORG 0003H???
AJMP? KEYINPUT ????????????? ;鍵盤輸入中斷
ORG 000BH?????
AJMP SETTIME???? ? ;采樣時間到,采樣轉(zhuǎn)速值中斷
ORG? 0100H
KEYINPUT:……??? ???? ????????? ?? ;鍵盤輸入中斷????
?????????????……????? ??????????????;將鍵盤輸入信號保存在
????????????????????????????????????????;70h~73h的地址空間中
RETI????????
ORG 0600H
SETTIME:……? ? ;采樣時間到,采樣轉(zhuǎn)速值中斷
??????? ?????? ……????????????? ??;將轉(zhuǎn)速值放置在地址為3Ah的空間中
????????????????????????? ?? ;緊接著調(diào)用外部C51函數(shù)CALL1()進
??????????????????????????? ??;行車速的計算
LCALL CALL1??????????
RETI
ORG?? 2000H?? ???? ??? ;主程序模塊
MAIN: ......????????????????????????;首先進行初始化操作
?????? ?? ......??
????????????????????????????????????????;直接從地址空間70h~77h中讀取顯示數(shù)據(jù),循環(huán)顯示車速和輪徑值
END
這些小模塊用匯編實現(xiàn)起來不僅容易,而且程序員可以清楚地了解到各個模塊的出入口及其相應的功能,實現(xiàn)對程序空間的充分配置。最后用C51語言來實現(xiàn)車速的計算模塊CALL1()。以前用匯編編寫的近400行代碼,一下子被壓縮到20~30行(真正的計算代碼僅9行),不僅簡短易懂,而且?guī)缀蹙筒恍枰{(diào)試了。
下面的代碼是計算模塊CALL1()及其需要的絕對地址定義。
#pragma code small
#include
#include
#define PI 3.1415926
#define? NCIRCLE DBYTE[0x3A]?? ;定義放置轉(zhuǎn)速的絕對地址
#define? DIRECT1 DBYTE[0x70]?? ;定義放置輪徑千位的絕對地址
#define? DIRECT2 DBYTE[0x71]?? ;定義放置輪徑百位的絕對地址
#define? DIRECT3 DBYTE[0x72]?? ;定義放置輪徑十位的絕對地址
#define? DIRECT4 DBYTE[0x73]?? ;定義放置輪徑個位的絕對地址
#define? VELOCITY1 DBYTE[0x74] ;定義返回車速的千位絕對地址
#define? VELOCITY2 DBYTE[0x75] ;定義返回車速的百位絕對地址
#define? VELOCITY3 DBYTE[0x76] ;定義返回車速的十位絕對地址
#define? VELOCITY4 DBYTE[0x77]?;定義返回車速的個位絕對地址
void call1( )
{
?????? float data result;
?????? int data DIRECT;
?????? DIRECT=DIRECT1*1000+DIRECT2*100+DIRECT3*10
??????????? +DIRECT4;
?????? result=(DIRECT/1000.0)*PI*NCIRCLE*3.6;
?????? VELOCITY1=result/100;
?????? result=result-VELOCITY1*100;
?????? VELOCITY2=result/10;
?????? result=result-VELOCITY2*10;
?????? VELOCITY3=result;
?????? result=result-VELOCITY3;
?????? VELOCITY4=result*10;
}
??? 在本例中定義了絕對地址空間70H~77H和3AH。其中3AH存放采樣轉(zhuǎn)速值輸入模塊輸入的轉(zhuǎn)速;70H~73H的地址空間中存放鍵盤輸入中斷模塊中鍵盤輸入的輪徑值;而地址為74H~77H的空間中則存放計算模塊中的車速計算返回值。盡管需要傳遞
和返回的參數(shù)比較多,但通過這些絕對地址的定義,完全解決了原來復雜的匯編與C51之間的調(diào)用接口配置。計算模塊中需要使用轉(zhuǎn)速和輪徑值時,將自動從絕對地址3AH和70H~73H中取值;在循環(huán)顯示車速和輪徑值的主程序模塊中則直接讀取絕對地址空間70H~77H的各個數(shù)據(jù)進行循環(huán)顯示。當然,程序員可以根據(jù)自己的空間配置另外定義這些絕對地址。
以上程序代碼均已在Dais-52.196P仿真器上順利調(diào)試通過。
由上面的簡單程序可以看出這種無參數(shù)化調(diào)用方法的優(yōu)越性和有效性:從程序代碼看,無論是編寫C51子程序還是匯編主程序,都與編寫純C51函數(shù)或者純匯編主程序的格式完全一樣,從根本上簡化了C51與匯編函數(shù)之間的接口編程,提高了程序調(diào)用的效率;充分利用了匯編與高級C51語言各自的優(yōu)點,開發(fā)、調(diào)試快速方便,通用性強,尤其適合于初學者。對于復雜程序,同樣可以利用無參數(shù)化方法來幫助實現(xiàn)。這對于提高單片機應用程序的開發(fā)效率很有意義。
無參數(shù)化調(diào)用實質(zhì)上在C51函數(shù)中定義了幾個全局變量(絕對地址),依靠它們直接完成參數(shù)值的傳遞和返回值的調(diào)用,相當于一種程序員自定義的傳遞方式,拋棄了傳統(tǒng)C與匯編之間的接口約定。只要程序員安排得當,還可以進一步人工實現(xiàn)C51中的動態(tài)覆蓋重用,提高RAM區(qū)的利用效率。由上也可看出:無參數(shù)化調(diào)用方法要在ASM匯編調(diào)用C51函數(shù)時才能充分發(fā)揮其巨大優(yōu)勢;如果全部采用C51編程,過分依賴無參數(shù)化思想,就違背了利用匯編優(yōu)勢的初衷,得不償失。當然,如果開發(fā)人員已經(jīng)對C51與匯編函數(shù)之間的參數(shù)傳遞接口很熟悉,完全可以按接口約定或者由編譯器自動完成參數(shù)的傳遞。
?
參考文獻
1 徐愛鈞.彭秀華.單片機高級語言C51應用程序設(shè)計.北京:電子工業(yè)出版社,1998
2 Dais系列組合式仿真編程器使用手冊.1999