<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > C51優(yōu)化設(shè)計(jì)之循環(huán)語句

          C51優(yōu)化設(shè)計(jì)之循環(huán)語句

          作者: 時(shí)間:2016-11-29 來源:網(wǎng)絡(luò) 收藏
          C51有三種循環(huán)語句即while,do-while和for,這三種循環(huán)都可以用來處理同一問題,基本上三者可以相互替換.但由于C51是針對(duì)51匯編語言的編譯器,如果不注意51匯編指令的特點(diǎn),不同的編程方式可能得到不同的程序性能(執(zhí)行速度和代碼長(zhǎng)度).以計(jì)算1+2+3+...+9+10為例,下面做一對(duì)比.
          程序1:unsigned char i;unsigned char sum;for(i=1,sum=0;i<11;i++){sum+=i;}匯編代碼為:C:0x0003 7F01 MOV R7,#0x01C:0x0005 E4 CLR AC:0x0006 FE MOV R6,AC:0x0007 EF MOV A,R7C:0x0008 2E ADD A,R6C:0x0009 FE MOV R6,AC:0x000A 0F INC R7C:0x000B BF0BF9 CJNE R7,#0x0B,C:0007代碼長(zhǎng)度(字節(jié)):11,執(zhí)行周期(機(jī)器周期):63程序2:unsigned char i;unsigned char sum;for(i=10,sum=0;i;i--){sum+=i;}匯編代碼為:C:0x000F 7F0A MOV R7,#0x0AC:0x0011 E4 CLR AC:0x0012 FE MOV R6,AC:0x0013 EF MOV A,R7C:0x0014 2E ADD A,R6C:0x0015 FE MOV R6,AC:0x0016 DFFB DJNZ R7,C:0013代碼長(zhǎng)度(字節(jié)):9,執(zhí)行周期(機(jī)器周期):53程序3:unsigned char i=11;unsigned char sum=0;while(i--){sum+=i;}匯編代碼為:C:0x0003 7F0A MOV R7,#0x0BC:0x0005 E4 CLR AC:0x0006 FE MOV R6,AC:0x0007 AD07 MOV R5,0x07C:0x0009 1F DEC R7C:0x000A ED MOV A,R5C:0x000B 6005 JZ C:0012C:0x000D EF MOV A,R7C:0x000E 2E ADD A,R6C:0x000F FE MOV R6,AC:0x0010 80F5 SJMP C:0007代碼長(zhǎng)度(字節(jié)):15,執(zhí)行周期(機(jī)器周期):130

          從以上三個(gè)不同程序可以看出,其運(yùn)算結(jié)果都是0x37(55),但最短代碼為9,最長(zhǎng)代碼為15,最快速度為53,最慢速度為130,可見三個(gè)程序的性能差異較大.

          本文引用地址:http://www.ex-cimer.com/article/201611/323326.htm

          如何編出占用空間小運(yùn)行效率高的循環(huán)代碼呢?在C51編譯環(huán)境下要寫出優(yōu)秀的循環(huán)代碼必須熟悉51匯編語言的指令系統(tǒng).觀察程序2,循環(huán)控制指令使用了DJNZ循環(huán)轉(zhuǎn)移指令,該指令同時(shí)完成計(jì)數(shù)和循環(huán)判斷兩種操作,而且只占用兩個(gè)字節(jié),是51指令系統(tǒng)中最為高效的循環(huán)指令,因此在設(shè)計(jì)循環(huán)程序時(shí),應(yīng)盡可能使C51將DJNZ用于循環(huán)程序中.當(dāng)然DJNZ指令的循環(huán)次數(shù)是確定的,主要用在有確定循環(huán)次數(shù)的情況.

          DJNZ指令的一個(gè)最大特點(diǎn)是遞減計(jì)數(shù),因此循環(huán)程序必須采用遞減方式才有可能編譯出DJNZ指令,如以上程序2.DJNZ指令的另一個(gè)特點(diǎn)是先減后判斷,因此設(shè)計(jì)循環(huán)程序也必須堅(jiān)持先減后判斷的原則,否則得不到DJNZ指令,如以上程序3.如果將程序3改寫為:
          unsigned char i=10;
          unsigned char sum=0;
          while(i)
          {
          sum+=i;
          i--;
          }
          就可以得到與程序2相同的匯編代碼.若i--后還有其它操作,比如改為:
          unsigned char i=10,j=0;
          unsigned char sum=0;
          while(i)
          {
          sum+=i;
          i--;
          j++;
          }
          也得不到DJNZ匯編指令,也就是說,循環(huán)語句在執(zhí)行過程中,減1與判斷必須是連續(xù)的,且減1在前,判斷在后.對(duì)于while循環(huán),當(dāng)將減1與判斷合成一步時(shí),應(yīng)當(dāng)采用while(--i).按照以上所述,do-while循環(huán)同樣可以匯編出DJNZ指令,不再一一列舉.

          但是當(dāng)循環(huán)變量不是通過常數(shù)賦值語句完成,而是來自于另一個(gè)變量時(shí),for和while語句無論采用何種控制流程都不能產(chǎn)生DJNZ指令,因?yàn)檫@兩種循環(huán)都是先判斷后執(zhí)行的控制邏輯,而DJNZ的執(zhí)行過程是先執(zhí)行循環(huán)體后進(jìn)行循環(huán)判斷.按照DJNZ的控制流程,只有do-while語句符合這個(gè)條件,因此當(dāng)循環(huán)次數(shù)不是常量而是變量時(shí),就必須使用do-while循環(huán)語句了.

          綜上所述,若要使用DJNZ指令提高程序效率,在設(shè)計(jì)循環(huán)程序中應(yīng)堅(jiān)持以下三大原則:
          ① 采用遞減計(jì)數(shù);
          ② 先減后判斷,減與判斷連續(xù)進(jìn)行;
          ③ 循環(huán)次數(shù)為變量時(shí),采用do-while循環(huán).
          8051單片機(jī)有兩條循環(huán)指令,即DJNZ Rn,rel和DJNZ direct,rel.對(duì)于基本型單片機(jī)而言,兩者的執(zhí)行時(shí)間都是2個(gè)機(jī)器周期,但兩者的指令長(zhǎng)度不同,前者占用2個(gè)字節(jié),后者占用3個(gè)字節(jié).循環(huán)程序還涉及到循環(huán)變量初始化操作,對(duì)于前者使用MOV Rn,#XX,2字節(jié)1周期,對(duì)于后者使用MOV direct,#XX,3字節(jié)2周期.以單層循環(huán)為例,使用工作寄存器比直接地址節(jié)省2字節(jié)1周期.除此之外,兩者相比,更重要的性能差異在于后者需要再分配一個(gè)內(nèi)存單元.因?yàn)橥ǔ3绦蚰K都使用工作寄存器作局部變量,將工作寄存器用作循環(huán)變量不會(huì)增加內(nèi)存占用量.總之,使用工作寄存器作循環(huán)計(jì)數(shù)器是設(shè)計(jì)循環(huán)程序應(yīng)堅(jiān)持的一項(xiàng)重要原則.

          一般情況下,C51編譯器將循環(huán)次數(shù)賦予工作寄存器,比如
          unsigned char i;
          for(i=100;i;i--)
          {
          dosomething();
          }

          但是存在下述情況之一時(shí),C51編譯的結(jié)果往往令人不滿意:
          ① 函數(shù)dosomething是一個(gè)外部定義C語言函數(shù);
          ② 函數(shù)dosomething是一個(gè)具有C語言接口,內(nèi)部用匯編語言實(shí)現(xiàn)的,供C程序調(diào)用的外部函數(shù).
          以上兩種情況循環(huán)變量i都存放在內(nèi)存單元中,即采用直接尋址方式.對(duì)于局部變量i,C51編譯器采用了直接地址存儲(chǔ),其原因在于基于這種假設(shè),即在無任何特殊處理的情況下,C51默認(rèn)外部函數(shù)占用所有工作寄存器,因此在循環(huán)的外部,不能修改這些已被占用的寄存器,C51只能將循環(huán)控制變量分配在內(nèi)存地址單元中.但如果循環(huán)體語句中僅使用少數(shù)幾個(gè)或甚至根本不使用工作寄存器,編譯器仍按這種假設(shè)處理,那么編譯器就不能顯現(xiàn)出它的高效性了.幸運(yùn)的是,C51提供了彌補(bǔ)這一缺陷的偽指令REGUSE.REGUSE偽指令用于告知編譯器某函數(shù)或子程序占用了哪些寄存器或特殊功能寄存器SFR,編譯器根據(jù)函數(shù)提供的寄存器占用信息就可能將循環(huán)變量分配到循環(huán)體未占用的寄存器中,從而達(dá)到優(yōu)化設(shè)計(jì)的目的.

          另外,一項(xiàng)開關(guān)必須打開,即Global Register Coloring,方法是勾選Project - Options for Target - C51 - Global Register Coloring.

          在情況①中,應(yīng)在函數(shù)dosomething所在源程序文件中添加代碼(假設(shè)函數(shù)占用A和B):
          #pragma asm
          $REGUSE dosomething(A,B)
          #pragma endasm
          重新編譯項(xiàng)目后,在匯編窗口中可以看到,循環(huán)變量已使用了工作寄存器.
          在情況②中,由于是匯編程序,只需增添一行代碼(也假設(shè)子程序占用A和B):
          $REGUSE dosomething(A,B)
          同樣可以觀察到循環(huán)變量改成了工作寄存器實(shí)現(xiàn).

          需要注意的是,這里所說的寄存器占用是指在函數(shù)或子程序執(zhí)行過程中可能或肯定對(duì)這些寄存器造成破壞,即執(zhí)行寫操作,對(duì)于只讀寄存器不應(yīng)按占用處理.另外,參數(shù)傳遞使用的工作寄存器不必指明.



          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();