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