關(guān)于Keil C51中using關(guān)鍵字的使用心得
后來調(diào)試了好久,甚至到http://www.51hei.com/keil%CF%C2%D4%D8.html這里下載了好幾個(gè)版本的keil,反匯編代碼看了N次,基本各個(gè)版本出來的結(jié)果都差不多,不知情的情況下,推斷出是Keil C51的編譯器問題,以為Keil C51的編譯器的變量空間分配出問題了,R7本來是用來作參數(shù)傳遞的,卻也被編譯器分配用來在主函數(shù)中被用來作循環(huán)變量,結(jié)果就是循環(huán)變量的匯編判斷指令CJNE指令執(zhí)行前,R7的值(也就是循環(huán)變量)被改變了,導(dǎo)致判斷R7總是不能達(dá)到循環(huán)設(shè)置的值,就不斷地在循環(huán),也就致使按鍵掃描去抖動這個(gè)延時(shí)循環(huán)無法完成,就導(dǎo)致延時(shí)后的第二次判斷無法進(jìn)行,自然按鍵下來也就沒反應(yīng)了。后來嘗試著關(guān)了定時(shí)器中斷,又可以了,那就可以肯定是在執(zhí)行CJNE前,R7的值被改變了,就是因?yàn)樘搅酥袛啵缓笾袛嗪瘮?shù)執(zhí)行的過程中,R7值被改變了(因?yàn)橹袛嗪瘮?shù)中還調(diào)用了其它的函數(shù),而R7后來被分析反匯編代碼時(shí)發(fā)現(xiàn)是被用作參數(shù)傳遞的),中斷執(zhí)行完后R7的值當(dāng)然不正確啦!
得出推斷后我的解決辦法就是:讓C51編譯好之后手動修改匯編的指令,不要直接使用寄存器來作循環(huán)變量,而是使用能實(shí)現(xiàn)間接尋地址的R1進(jìn)行間接尋址(指向0x77)的方式,終于把問題解決了,因?yàn)?x77在執(zhí)行中斷函數(shù)的過程中沒被修改,CJNE語句能正常判斷了,循環(huán)能完成了,按鍵也能掃下來了,問題也可以解決了。后來我又想到用_at_關(guān)鍵字直接指定循環(huán)變量的分配地址,這樣的話就不用手動改匯編指令了,編譯器直接就是編譯成使用間接尋址的,這也算解決了。但還是有個(gè)疑問,為什么編譯器會不夠聰明地把R7的空間分配錯(cuò)了呢?一直在疑問。。。。。。奇怪,怎么編譯器會出這種問題?
直到我剛才考究過了using的用法之后,才發(fā)現(xiàn)原來自己的推斷是錯(cuò)的,結(jié)論的總方向還是對的,只是問題并不在編譯器身上,真正的問題根源如下:
1.我的同學(xué)寫的那個(gè)程序,用到了定時(shí)器0,它的中斷服務(wù)函數(shù)定義是這樣寫的:
void t0(void) interrupt 1 using 0
{
........//一大串大代碼,其中包括調(diào)用了其他函數(shù),用到了R7作參數(shù)傳遞
}
這里就是加了個(gè)using 0,而問題就出在加了這個(gè)using 0,為什么?且聽我的理解:
using關(guān)鍵字的作用就是指定某個(gè)函數(shù)在執(zhí)行時(shí)切換寄存器組的:
51中有四個(gè)寄存器組,每個(gè)組有R0-R7這8個(gè)寄存器,用于CPU的數(shù)據(jù)處理,一般主函數(shù)main()默認(rèn)使用第0組,因?yàn)镻SW寄存器的初始值的第3、4位是00嘛,即默認(rèn)指定了第0組。using 0就是指定在執(zhí)行函數(shù)時(shí)切換為使用第0組,不加using關(guān)鍵字的話,一般都默認(rèn)使用第0組,但這樣的話在調(diào)用其他函數(shù)(包括中斷服務(wù)函數(shù))的時(shí)候,就會加入一些壓棧指令以保護(hù)原來的R0-R7寄存器的(這樣的話,程序執(zhí)行會效率會低一點(diǎn),因?yàn)闀a(chǎn)生很多個(gè)指令是用來壓棧出棧的),照這樣說,只是加了using 0,使用的也是第0組又不是其它的組,程序就不應(yīng)該有問題了吧,但是就是因?yàn)榧恿藆sing 0,編譯了一次,才發(fā)現(xiàn)在定時(shí)器中斷函數(shù)t0()的入口中并沒有發(fā)現(xiàn)把R0-R7的代碼壓入棧呀,就是說沒有保護(hù)好R7呀,那當(dāng)然就是在執(zhí)行完之后回來R7不能回復(fù)原來的值啦,接下來的事情。。。。我就不說啦,這就是問題的根源,去掉using 0就可以了,編譯器就自動幫你將R0-R7壓棧,手動加了using 0,就是讓編譯器以為之前用的并不是第0組,而現(xiàn)在執(zhí)行這個(gè)中斷函數(shù)時(shí)就切換到第0組,而省去了將R0-R7這8個(gè)寄存器壓入棧的指令了,這樣雖然看起來是快了,然而對于這個(gè)程序來說卻是致命的問題?。?!因?yàn)楦緵]有保護(hù)好R0-R7,而沒有保護(hù)R7并不是我預(yù)期發(fā)生的!?。∵@不是編譯器的問題,是自己沒有了解好、用好using的問題啊!還有,其實(shí)也可以在編譯器選項(xiàng)里有選項(xiàng)來硬性規(guī)定編譯器統(tǒng)一不直接使用寄存器而是使用間接尋址的辦法來改變循環(huán)變量分配的地址的,或者使用
#pragma NOAREGS
定義函數(shù)
#pragma AREGS
來規(guī)定某個(gè)函數(shù)的是這樣子。
當(dāng)然,這樣的規(guī)定和使用using本身并不沖突,只是使用了這個(gè)關(guān)鍵字后就可能會間接地產(chǎn)生一系列的問題,讓人郁悶了這么久,其實(shí)本來如果有詳細(xì)地看整個(gè)程序的反匯編代碼,或許當(dāng)時(shí)就會發(fā)現(xiàn)發(fā)現(xiàn)少了那段壓棧指令了,而不是說推斷為編譯器分配空間的問題了。。。。。。。。希望我的這次教訓(xùn)對各位有所幫助!??!
另外,using的用法,其實(shí)就是手動指定函數(shù)使用的寄存器組,用得不好,如果在中斷里還有調(diào)用其它函數(shù),用得不好會出現(xiàn)函數(shù)傳遞出錯(cuò)的,不信可以反匯編看看,建議如果對這個(gè)關(guān)鍵字用法和C51的結(jié)構(gòu)及匯編不熟的話,請還是讓C51編譯器幫你好了,不要胡亂使用,因?yàn)闀容^容易出錯(cuò)的,要切記哦?。。∑鋵?shí)用using關(guān)鍵到底對在編譯后會造成什么影響,建議自己親自去查看匯編程序。。。
評論