<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > c51軟復位經(jīng)典分析

c51軟復位經(jīng)典分析

——
作者: 時(shí)間:2007-04-05 來(lái)源: 收藏

從單片機指針說(shuō)到黑客程序

 

在電子BBS討論區上溜達,看到一個(gè)有趣的帖子,整個(gè)帖子內容如下:

純C51復位功能函數:一個(gè)大三學(xué)生,讓人又愛(ài)又怕

現單列復位部分如下:

main()

{

  &nbspunsigned&nbspchar&nbspcode&nbsprst[]={0xe4,0xc0,0xe0,0xc0,0xe0,0x32};  // 復位代碼

   (*((void (*)())(rst)))();  // 執行上一行代碼,將rst數組當函數調用

}

本來(lái)我告訴他嵌入如下代碼:

clr&nbspa

push&nbspacc

push&nbspacc

reti

結果他卻玩了前面哪一段,而數組rst[]中的內容恰恰是上面的匯編機器碼,他的做法是將rst數組的數據當作代碼保存,然后采用絕對地址方式指向該數組,將該數組中的代碼當作函數來(lái)運行。居然通過(guò)了!

我覺(jué)得有問(wèn)題,我說(shuō)即使如此,那絕對地址調用也應該寫(xiě)成(*((void (*)())(&rst)))() 才對呀,結果他反駁說(shuō),那樣的話(huà),rst的地址就會(huì )當成參數傳遞給這個(gè)絕對地址函數,而實(shí)際LJMP調用的地址并非rst的地址,而是一個(gè)不確定的地址。于是我按照自己的說(shuō)法嘗試了一下,看看匯編結果,還真的是將rst的地址傳遞給了R1&nbspR2,而絕對函數最終LJMP到了
一個(gè)莫名其妙的地址上去了,死翹!

看來(lái)C真是一匹不容易駕馭的野馬,這個(gè)大三學(xué)生理解力在我之上,我30多歲的人了,干了這么多年還沒(méi)他的境界呢,唉,人家才學(xué)了幾天啊,翻了幾天書(shū)就這么厲害了,服了!

 

l         首先分析帖子的C語(yǔ)言代碼

第一句定義一個(gè)數組rst[],數組內數據就是完成復位功能的匯編機器碼,具體對應關(guān)系
為:clr&nbspa ==&nbsp0xe4、push&nbspacc ==&nbsp0xc0,0xe0、reti ==0x32

第二句是一個(gè)函數指針的用法,函數指針用法稍微有點(diǎn)復雜,可參看本人著(zhù)的書(shū),:),以
下為快速入門(mén)講解。

定義一個(gè)返回值是空函數指針的定義形式如下:

void (*p) ( )

當把函數指針賦值后,就能通過(guò)函數指針調用函數,調用形式如下,

      (*p) ( );

或等價(jià)的簡(jiǎn)化形式:

p ( );

假設rst就是函數指針,則如下調用形式就可以令單片機復位再起。

(*rst ) ( ); 

但可惜,rst不是函數指針,而是數組名,雖然兩者都是地址,但不可直接調用數組名。

如同把char型變量a賦值給int型變量b,(int) 表示強制類(lèi)型轉換:

b = (int)&nbspa

函數指針的強制類(lèi)型轉換公式如下(C語(yǔ)言的哲學(xué)是定義形式和使用一致):

(  (void (*)()  )&nbsprst

這樣經(jīng)過(guò)轉換后的rst就可以當作函數指針使用了,簡(jiǎn)單的調用形式如下:

#define &nbspK     (  (void (*)( )  )&nbsprst

(*K) ( )

或:

(     * ( &nbspvoid (*)( )  )rst      ) ( );

這樣的語(yǔ)句就完成復位再啟功能了。類(lèi)型轉換符()的優(yōu)先級跟指針運算符*的優(yōu)先級相同,
二者的結合方向是自右至左,所以上述語(yǔ)句就能完成復位功能了。保險起見(jiàn)有些程序員常
常喜歡再加個(gè)括號:

#define &nbspK     (   (  (void (*)( )  )&nbsprst   )

(*K) ( )

(     *(   ( &nbspvoid (*)( )  )rst   )    ) ( );

 

由于沒(méi)有輸入參數,上述復位代碼更嚴謹的寫(xiě)法是:

#define &nbspK     (   (  (void (*)(void )  )&nbsprst   )

(*K) ( )

(     *(   ( &nbspvoid (*)(void )  )rst   )    ) ( );

 

關(guān)于帖子作者的解釋

千萬(wàn)不要犯“&rst”形式的錯誤,對于一維數組而言,數組名rst就代表地址。以下二者等
價(jià),更常用的是等式左邊的形式:

rst == &rst[0]

整個(gè)函數指針無(wú)所謂參數傳遞,只是把rst當作程序執行地址調用而已,那個(gè)學(xué)生的解釋也
有問(wèn)題。

還有一點(diǎn)必須提及,不是說(shuō)能通過(guò)編譯,甚至生成正確代碼,就表示某語(yǔ)句一定是對的。
對很復雜的語(yǔ)句,要考慮到編譯器不嚴格甚至出錯的可能性。

 

哈佛結構和一個(gè)蠕蟲(chóng)病毒

請注意,定義數組rst[]時(shí)用了關(guān)鍵字code,這是C51特有的關(guān)鍵字,意味著(zhù)把數組定義到程序空間。標準C是沒(méi)有關(guān)鍵字code的。

哈佛結構和普林斯頓結構:

哈佛結構——程序空間和存儲空間分開(kāi)的。C51算是不太嚴格的哈佛結構——雖地址線(xiàn)分開(kāi),但數據線(xiàn)沒(méi)有分開(kāi)。DSP是增強的哈佛結構。

PC電腦上奔騰CPU是普林斯頓結構——數據空間和程序空間統一編址。

 

如果數組rst[]數據的匯編機器碼是刪除文件的機器碼,這算不算是病毒?

曾經(jīng)流行過(guò)一種蠕蟲(chóng)病毒,其發(fā)作機理采取的就是將惡意代碼保存成文本文件,然后通過(guò)指針調用執行這個(gè)文本,很多殺毒程序也不會(huì )查詢(xún)文本文件。

程序也罷,數據也罷都是二進(jìn)制形式,如果數據空間和程序空間是統一編碼的, 數據當然可以當作程序運行。

在這一點(diǎn)上,相對而言,哈佛結構的CPU安全性會(huì )好一點(diǎn)點(diǎn)。但嵌入式應用少有病毒,一般不用關(guān)心。

 

單片機復位的更好方法

帖子中匯編語(yǔ)言解釋如下:

clr&nbspa                      //清除ACC=0

push&nbspacc               //壓0到堆?!?位

push&nbspacc               //再壓0到堆?!?位

reti                        //返回到0地址,從而執行。

帖子作者的這種復位方法比較麻煩,更加簡(jiǎn)單的復位寫(xiě)法是(摘自《C缺陷與陷阱》):

(     * ( &nbspvoid (*)( )  )0      ) ( );

本句的分析方法同上,但更加精煉,沒(méi)有多余的匯編語(yǔ)句。

 

上述復位的方法可稱(chēng)為軟件復位。

軟件復位跟真正上電復位有很大差別:上電復位時(shí)大部分寄存器都有確定的復位值;軟件復位則只相當于從0地址開(kāi)始執行而已,寄存器不會(huì )變?yōu)榇_定的復位值。

如果用戶(hù)要編程實(shí)現上電復位這種情況,在程序中不要踢看門(mén)狗即可。大部分單片機都有看門(mén)狗吧。

 


----------------2---------------


匯編中的ORG&nbsp0X0000H 在C51中如何實(shí)現.
一般是在連接定位模塊中來(lái)進(jìn)行地址分配
1、選擇"options&nbspfo&nbsptarget   "
2、選擇“BL51&nbspLocate”
3、在code 欄填入   ?PR?MAIN?SS(0x800)

其中MAIN是你要定位的函數名,SS是函數所在的文件名,要是有多個(gè)函數需要定位,則在中間加逗號。

 

功能強大的時(shí)鐘中斷
  在單片機程序設計中,設置一個(gè)好的時(shí)鐘中斷,將能使一個(gè)CPU發(fā)揮兩個(gè)CPU的功效,大大方便和簡(jiǎn)化程序的編制,提高系統的效率與可操作性。我們可以把一些例行的及需要定時(shí)執行的程序放在時(shí)鐘中斷中,還可以利用時(shí)鐘中斷協(xié)助主程序完成定時(shí)、延時(shí)等操作。


  下面以6MHz時(shí)鐘的AT89C51系統為例,說(shuō)明時(shí)鐘中斷的應用。
  定時(shí)器初值與中斷周期 時(shí)鐘中斷無(wú)需過(guò)于頻繁,一般取20mS(50Hz)即可。如需要百分之一秒的時(shí)基信號,可取10mS(100Hz)。這里取20mS,用定時(shí)器T0工作于16位定時(shí)器方式(方式1)。


    T0的工作方式為:每過(guò)一個(gè)機器周期自動(dòng)加1,當計滿(mǎn)0FFFFh,要溢出時(shí),便會(huì )產(chǎn)生中斷,并由硬件設置相應的標志位供軟件查詢(xún)。即中斷時(shí)比啟動(dòng)時(shí)經(jīng)過(guò)了N+1個(gè)機器周期。所以,我們只要在T0中預先存入一個(gè)比滿(mǎn)值0FFFFh小N的數,然后啟動(dòng)定時(shí)器,便會(huì )在N個(gè)機器周期后產(chǎn)生中斷。


    這個(gè)值便是所謂的“初值”。下面計算我們需要的初值:時(shí)鐘為6MHz,12個(gè)時(shí)鐘周期為一個(gè)機器周期,20mS中有10000個(gè)機器周期。(10000)10=(2710)16,0FFFFh-2710h+1=0D8F0h。由于響應中斷、保護現場(chǎng)及重裝初值還需要7~8個(gè)機器周期,把這個(gè)值再加上7,即T0應裝入的初值是0D8F7h。每次中斷進(jìn)入后,先把A及PSW的值壓入堆棧,然后即把0D8F7h裝入T0。


  設置一個(gè)單元,每次中斷加1 我們可以取內部RAM中一個(gè)單元,取名為INCPI(Increase Per&nbspInterrupt),在中斷中,裝完T0初值后,用INC INCPI指令將其加一。從這個(gè)單元中,無(wú)論中斷程序還是主程序,都可以從中獲得20mS的1~256之間任意整數倍的信號。例如:有一段向
數碼管送顯的程序,需要每0.5秒執行一次以便刷新顯示器,便可以設一單元(稱(chēng)為等待單元)W_DISP,用/MOV&nbspA,INCPI/ADD&nbspA,#25/MOV&nbspW_DISP,A/語(yǔ)句讓其比當前的INCPI值大25,然后在每次中斷中檢查是否于INCPI值相等。若相等,說(shuō)明已過(guò)了25個(gè)中斷周期,便執行送顯程序,并且讓W(xué)_DISP再加上25,等待下個(gè)0.5秒。我們可以設置多個(gè)等待單元,以便取出多個(gè)不同的時(shí)基
信號。讓中斷程序在每次中斷時(shí)依次查詢(xún)各個(gè)等待單元是否與INCPI相等,若相等,則執行相應的處理,并重新設置該等待單元的值,否則跳過(guò)。例如:用0.5秒信號刷新或閃爍顯示器,用1秒信號產(chǎn)生實(shí)時(shí)時(shí)鐘,或輸出一定頻率的方波,以一定間隔查詢(xún)輸入設備等。


  在中斷中讀鍵 通常,我們在主程序中讀鍵盤(pán),步驟為:掃描鍵盤(pán),若有鍵按下,則延時(shí)幾十毫秒去抖動(dòng),再次確認此鍵確實(shí)按下,然后處理該鍵對應的工作,完成后再次重上述步驟。但這有兩點(diǎn)不足:1.處理相應工作時(shí)無(wú)法鎖存按鍵的輸入,即可能漏鍵。2.延時(shí)去抖時(shí)CPU無(wú)法做其它事情,效率不高。如果把讀鍵放入時(shí)鐘中斷中,則可避免上述不足。方法為:如果兩次相鄰的中斷中都讀到同一個(gè)鍵按下,則這個(gè)鍵是有效的(達到了去抖目的),并將其鎖存到先入先出(隊列)的鍵盤(pán)緩沖區,等主程序來(lái)處理。這樣,主程序處理按鍵的同時(shí),仍可響應鍵盤(pán)的輸入。


    緩沖區深度通??稍O為8級,若鎖存的鍵數多于8個(gè),則忽略新的按鍵,并報警提示用戶(hù)新的按鍵將無(wú)效。若鍵盤(pán)緩沖隊列停滯的時(shí)間大大長(cháng)于主程序處理按鍵所需要的最大時(shí)間,說(shuō)明主程序已
出錯或跑飛,可以在中斷用指令將系統復位,起到了看門(mén)狗的目的?! ?/P>


    主程序中的延時(shí) 由于有常開(kāi)的時(shí)鐘中斷,所以當主程序中有需要時(shí)間較短、精度較高的延時(shí)時(shí),應暫時(shí)把時(shí)鐘中斷關(guān)閉。而程序中需要時(shí)間較長(cháng)、精度不高的延時(shí)時(shí),便可仿照下需的寫(xiě)法,避免多層嵌套的循環(huán)延時(shí)。


  例:在P1.1輸出1秒的高電平脈沖
   &nbspMOV   &nbspA,INCPI
   &nbspINC   &nbspA
   &nbspCJNE   &nbspA,INCPI$    ;等待一次中斷處理完成
   &nbspSETB   &nbspP1.1        ;設P1.1為H,脈沖開(kāi)始
   &nbspADD   &nbspA,#50        ;50個(gè)20mS為1秒
   &nbspCJNE   &nbspA,INCPI,$    ;等中斷將INCPI加一50次
   &nbspCLR   &nbspP1.1        ;設P1.1為L(cháng),脈沖結束


  結束語(yǔ):從上看出,要靈活地應用時(shí)鐘中斷,將任務(wù)合理分配給中斷和主程序,并且二者要分工明確,接口簡(jiǎn)單。這其中的技巧還需要大家在實(shí)踐中多多摸索與體會(huì )。另外要注意:應盡量
縮短中斷處理程序的執行時(shí)間,更不要長(cháng)于20mS。

 

c語(yǔ)言相關(guān)文章:c語(yǔ)言教程




關(guān)鍵詞: c51

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>