?? 關(guān)鍵詞:ASM51 C51 無參數(shù)化調(diào)用
1 引 言
隨著計(jì)算機(jī)應(yīng)用技術(shù)的發(fā)展,MCS-51系列單片機(jī)在我國工業(yè)測(cè)控領(lǐng)域中的應(yīng)用日益廣泛。因而,如何快速有效地進(jìn)行MCS-51系列單片機(jī)的開發(fā)便成了大家頗為關(guān)心的話題。ASM51匯編語言雖然簡(jiǎn)潔高效,但晦澀難懂,不易編寫,它還要求開發(fā)人員對(duì)硬件亦有相當(dāng)?shù)牧私?。相比而言,專為MCS-51系列單片機(jī)設(shè)計(jì)的Franklin C51語言是一種通用的高級(jí)結(jié)構(gòu)化的程序設(shè)計(jì)語言,入門容易,程序可讀性強(qiáng),調(diào)試、移植都很方便,開發(fā)效率高,尤其在數(shù)值運(yùn)算處理等方面具有很大的優(yōu)勢(shì)(而這正是匯編語言的薄弱環(huán)節(jié))。因而,在效率為重的今天,將ASM51匯編語言與Franklin C51語言兩者結(jié)合起來進(jìn)行混合編程,充分發(fā)揮各自的優(yōu)勢(shì),無疑是單片機(jī)開發(fā)人員的最佳選擇。
筆者曾開發(fā)過一系統(tǒng),要求用單片機(jī)根據(jù)實(shí)時(shí)采樣輸入的轉(zhuǎn)速實(shí)現(xiàn)機(jī)車速度的測(cè)量,并可隨鍵盤輸入的車輪直徑變化實(shí)時(shí)調(diào)整車速,最后將車速和輪徑值都顯示出來。設(shè)計(jì)任務(wù)很簡(jiǎn)單,編程中的最大難度就在于車速的計(jì)算程序。由于輪徑值要求精確到毫米(最大值超過了1000),車速的計(jì)算結(jié)果要保留到小數(shù)點(diǎn)后一位。因此必然要進(jìn)行浮點(diǎn)數(shù)運(yùn)算,另外還涉及到數(shù)據(jù)的各種進(jìn)制間的換算。雖然算法簡(jiǎn)單,但實(shí)際運(yùn)用時(shí),用匯編語言實(shí)現(xiàn)起來經(jīng)??紤]不周,調(diào)試起來費(fèi)時(shí)費(fèi)力。那如何利用C51語言來幫助簡(jiǎn)化編程呢?
2 ASM51匯編語言與C51語言的混合編程
一般的混合編程都是利用C51上手容易、便于理解的優(yōu)點(diǎn)來編寫主程序,同時(shí)在C51語言不便處理或者效率比較低時(shí)調(diào)用匯編函數(shù)。MCS-51單片機(jī)(尤其是8031)內(nèi)部的資源配置情況如下:可用的RAM不到256字節(jié),5個(gè)固定地址的有限中斷,4個(gè)8位并口中往往實(shí)際可作I/O口的只有P1口。鑒于此,要求開發(fā)者對(duì)單片機(jī)內(nèi)部的結(jié)構(gòu)有清楚的了解,并盡可能地統(tǒng)籌安排這些資源。另外,事實(shí)證明不理解匯編語言是很難寫出高效程序的。故筆者傾向利用匯編語言在I/O接口、中斷向量及程序空間分配上一目了然的巨大優(yōu)勢(shì) ,讓程序員對(duì)MCS-51內(nèi)的每一個(gè)字節(jié)甚至是每一位Bite(可以位尋址的空間)進(jìn)行全面統(tǒng)籌安排,設(shè)計(jì)好各個(gè)程序模塊,包括I/O口地址的處理和中斷向量的地址等(而這些簡(jiǎn)短程序用ASM51匯編語言來寫并不困難);同時(shí)在具體的數(shù)據(jù)處理、通信等不太需要過多與硬件直接打交道的程序模塊中(這也正是匯編語言的薄弱環(huán)節(jié)),充分利用C51語言強(qiáng)大高效的編程能力。
?? 針對(duì)前面提出的問題,可先用ASM51匯編語言設(shè)計(jì)好各個(gè)模塊,包括“循環(huán)顯示車速和輪徑值”的主程序模塊、響應(yīng)“采樣轉(zhuǎn)速值”和“鍵盤輸入”兩個(gè)中斷的模塊,如例1所示(具體實(shí)現(xiàn)過程略)。
例1:
?? EXTRN CODE(CALL1);聲明外部C51函 數(shù)CALL1()
?? ORG 0000H?
?? LJMPMAIN
?? ORG 0003H
?? AJMPKEYINPUT;鍵盤輸入中斷
?? ORG 000BH
?? AJMPSETTIME;采樣時(shí)間到,采樣轉(zhuǎn)速值中斷
?? ORG 0100H
KEYINPUT:......;鍵盤輸入中斷......;將鍵盤輸入信號(hào)保存在70h-73h的地址空間中
?? RETI
?? ORG 0600H
?? SETTIME:......;采樣時(shí)間到,采樣轉(zhuǎn)速值中斷.....;將轉(zhuǎn)速值放置在地址為3Ah的空間中
;緊接著調(diào)用外部C51函數(shù)CALL1()進(jìn)行車速的計(jì)算
?? LCALLCALL1RETI
?? ORG 2000H;主程序模塊MAIN:......;首先進(jìn)行初始化操作......
;直接從地址空間70h-77h中讀取顯示數(shù)據(jù),循環(huán)顯示車速和輪徑值
?? END
這些小模塊用匯編語言實(shí)現(xiàn)起來不僅容易,而且程序員可以清楚地了解到各個(gè)模塊的出入口及其相應(yīng)的功能,實(shí)現(xiàn)對(duì)程序空間的充分配置。
??? 另外用C51語言來實(shí)現(xiàn)復(fù)雜的車速計(jì)算模塊CALL1(),結(jié)果以前用匯編語言編寫的近四百行代碼,一下子被壓縮到二三十行(真正的計(jì)算代碼僅九行,參見例2中的代碼)!不僅簡(jiǎn)短易懂,而且?guī)缀蹙筒恍枰{(diào)試了!最后的一個(gè)關(guān)鍵是如何讓匯編模塊正確識(shí)別C51函數(shù)CALL1()并調(diào)用它來完成相應(yīng)的功能。
3 ASM51“無參數(shù)化”調(diào)用C51函數(shù)的實(shí)現(xiàn)原理
為了實(shí)現(xiàn)函數(shù)調(diào)用時(shí)參數(shù)的正確傳送,一般都要在ASM51匯編語言與C51語言之間制定一系列約定,而各種編譯器使用的約定不盡相同,甚至還依賴于程序所選擇的大、中、小存儲(chǔ)模式。通常每個(gè)需傳遞的參數(shù)按調(diào)用順序和類型分別由約定的寄存器來傳遞;參數(shù)過多或者無足夠寄存器可用時(shí),參數(shù)的傳遞將在一定的存儲(chǔ)器區(qū)域內(nèi)進(jìn)行,相同類型的參數(shù)共享一個(gè)參數(shù)傳遞段,按參數(shù)調(diào)用順序遞增其存放地址,返回值也由約定的寄存器或地址段返回。由此可以想見,接口如何復(fù)雜,程序調(diào)用的效率也將受到影響。盡管目前的單片機(jī)仿真器已經(jīng)普遍提供了這種標(biāo)準(zhǔn)接口約定的幾乎全自動(dòng)轉(zhuǎn)換,減少了接口工作量,但在程序的調(diào)試及移植中,如果程序員不了解這些接口的各種約定,將對(duì)出現(xiàn)的錯(cuò)誤不知所措。比如在前面要實(shí)現(xiàn)的車速計(jì)算模塊CALL1()中,為便于其顯示模塊中操作簡(jiǎn)便,要求該模塊能直接按小數(shù)、個(gè)、十、百位返回車速,由于返回值有多個(gè)(另有兩個(gè)參數(shù)),故編譯器自己無法正確完成接口配置。而且由于這種接口編程與純C51語言或者純匯編語言有一定差別,難免造成初學(xué)者對(duì)此望而生畏(限于篇幅,這種帶參數(shù)調(diào)用函數(shù)的繁瑣實(shí)現(xiàn)請(qǐng)讀者參考其它資料 。)。經(jīng)過筆者的嘗試,這里向大家推薦一種簡(jiǎn)捷有效的調(diào)用方法——“無參數(shù)化”調(diào)用。
所謂的“無參數(shù)化”調(diào)用實(shí)現(xiàn)是指讓C51子函數(shù)不帶任何形參,這樣就可以從根本上避開調(diào)用參數(shù)的傳遞和返回值的安排等繁瑣易出錯(cuò)的問題,而只需要簡(jiǎn)單地在匯編語言開頭說明一下外部C51子函數(shù)(“EXTRN code(<C51模塊名稱>)”)。至于C51函數(shù)中需要使用的外部參數(shù)值及其返回值,則通過加入C51提供的<absacc.h>頭文件來解決。

??? 其中CBYTE定義為尋址CODE程序區(qū);DBYTE定義為尋址DATA數(shù)據(jù)區(qū);PBYTE定義為尋址相對(duì)于MOVX@R0"指令的分頁數(shù)據(jù)XDATA區(qū);XBYTE定義為尋址相對(duì)于MOVX@DPTR "指令的外部數(shù)據(jù)XDATA區(qū)。它們的類型決定了所訪問的絕對(duì)地址空間的位置。
引進(jìn)該頭文件后,程序員就可以對(duì)8051系列單片機(jī)的存儲(chǔ)器進(jìn)行絕對(duì)地址的訪問 ,把對(duì)參數(shù)值和返回值的操作轉(zhuǎn)化為對(duì)存儲(chǔ)器絕對(duì)地址的操作,像純匯編操作一樣,也就不用定義C51函數(shù)與匯編接口的參數(shù)和返回值配置,從而提高了調(diào)用效率。具體做法是:先在C51函數(shù)中定義好傳遞參數(shù)和返回值所需要的各個(gè)絕對(duì)地址(視程序員自己的空間配置而定,如例2中#define處所示,括號(hào)“〔〕”中即為8051的RAM絕對(duì)地址),在別的匯編模塊中將C51函數(shù)中將要使用的參數(shù)值放入這些絕對(duì)地址中,在被調(diào)用的C51模塊中將輸出的計(jì)算值(可以不止一個(gè))也同樣放入類似的絕對(duì)地址中。于是,當(dāng)C51函數(shù)中需要使用某個(gè)參數(shù)傳遞的值時(shí),就直接從相應(yīng)的絕對(duì)地址中讀取該值;當(dāng)別的匯編模塊中需要使用C51函數(shù)的返回值時(shí),也同樣直接對(duì)存放返回值的絕對(duì)地址進(jìn)行讀操作即可。筆者用該法輕松實(shí)現(xiàn)了對(duì)具有多個(gè)返回值的計(jì)算模塊CALL 1()的調(diào)用。
4 ASM51“無參數(shù)化”調(diào)用C51函數(shù)的實(shí)現(xiàn)示例
? 例2是計(jì)算模塊CALL1()及相應(yīng)絕對(duì)地址定義的代碼實(shí)現(xiàn)。
例2:
?? #pragma code small
?? #include<absacc.h>
?? #include<math.h>
#define PI3.1415926;以下定義幾個(gè)需要的絕對(duì)地址
#define NCIRCLE DBYTE〔0x3A〕;定義放置轉(zhuǎn)速的絕對(duì)地址
#define DIRECT1 DBYTE〔0x70〕;定義放置輪徑千位的絕對(duì)地址
#define DIRECT2 DBYTE〔0x71〕;定義放置輪徑百位的絕對(duì)地址
#define DIRECT3 DBYTE〔0x72〕;定義放置輪徑十位的絕對(duì)地址
#define DIRECT4 DBYTE〔0x73〕;定義放置輪徑個(gè)位的絕對(duì)地址
#define VELOCITY1 DBYTE〔0x74〕;定義返回車速的百位絕對(duì)地址
#define VELOCITY2 DBYTE〔0x75〕;定義返回車速的十位絕對(duì)地址
#define VELOCITY3 DBYTE〔0x76〕;定義返回車速的個(gè)位絕對(duì)地址
#define VELOCITY4 DBYTE〔0x77〕;定義返回車速的小數(shù)位絕對(duì)地址

?? 在例2中主程序前用9個(gè)define語句預(yù)定義了絕對(duì)地址空間70H-77H和3AH。其中3AH的空間中存放“采樣轉(zhuǎn)速值輸入”中斷模塊輸入的轉(zhuǎn)速;70H-73H的地址空間中存放“鍵盤輸入中斷”中斷模塊中鍵盤輸入的輪徑值;而地址為74H-77H的空間中則存放“計(jì)算”模塊中的車速計(jì)算返回值。盡管需要傳遞和返回的參數(shù)比較多,但通過這些絕對(duì)地址的定義,完全解決了原來復(fù)雜的匯編與C51之間的調(diào)用接口配置?!坝?jì)算”模塊中需要使用轉(zhuǎn)速和輪徑值時(shí),將自動(dòng)從相應(yīng)絕對(duì)地址3AH和70H-73H中取值 ;在“循環(huán)顯示車速和輪徑值”的主程序模塊中則直接讀取絕對(duì)地址空間70H-77H的各個(gè)數(shù)據(jù)進(jìn)行循環(huán)顯示。當(dāng)然,程序員可以根據(jù)自己的空間配置另外定義這些絕對(duì)地址 。
以上程序代碼已在“Dais-52.196P”仿真器上調(diào)試通過并正確運(yùn)行。
由上面的簡(jiǎn)單程序已經(jīng)可以看出這種“無參數(shù)化”調(diào)用方法的優(yōu)越性和有效性:從程序代碼看,無論是編寫C51子程序還是匯編主程序,都與編寫純C51函數(shù)或者純匯編主程序的格式完全一樣,簡(jiǎn)化了C51與匯編函數(shù)之間的接口編程,提高了程序調(diào)用的效率;同時(shí)充分利用了匯編與高級(jí)C51語言各自的優(yōu)點(diǎn),開發(fā)、調(diào)試快速方便,通用性強(qiáng),尤其適合于初學(xué)者。對(duì)于復(fù)雜程序,同樣可以利用“無參數(shù)化”方法來幫助實(shí)現(xiàn)。這對(duì)于加快單片機(jī)應(yīng)用程序的開發(fā)效率和普及是很有意義的。
5 結(jié)束語
? “無參數(shù)化”調(diào)用實(shí)質(zhì)上相當(dāng)于在C51函數(shù)中定義了幾個(gè)全局變量(絕對(duì)地址),依靠他們直接完成參數(shù)值的傳遞和返回值的調(diào)用,相當(dāng)于一種程序員自定義的傳遞方式,而拋棄了傳統(tǒng)C語言與匯編語言之間的接口約定。只要程序員安排得當(dāng),還可以進(jìn)一步人工實(shí)現(xiàn)C51中的“動(dòng)態(tài)覆蓋重用”,提高RAM區(qū)的利用效率。由上也可看出:“無參數(shù)化”調(diào)用方法要在AS M匯編調(diào)用C51函數(shù)時(shí)才能充分發(fā) 揮其巨大優(yōu)勢(shì);如果全部采用C51編程,就違背了利用匯編語言優(yōu)勢(shì)的初衷。當(dāng)然,如果開發(fā)人員已經(jīng)對(duì)C51與匯編函數(shù)之間的參數(shù)傳遞接口很熟悉,那么也完全可以按接口約定或者由編譯器自動(dòng)完成參數(shù)的傳遞。
評(píng)論