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

          新聞中心

          arm匯編編程(示例)

          作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò) 收藏
          一、arm的認(rèn)知及基本概念

          (一).arm的基本概念

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

          1.什么是arm

          arm是一家英國(guó)電子公司的名字,全名是AdvancedRISCMachine

          這家企業(yè)設(shè)計(jì)了大量高性能、廉價(jià)、耗能低的RISC(精簡(jiǎn)指令集)處理器,ARM公司只設(shè)計(jì)芯片而不生產(chǎn),它將

          技術(shù)授權(quán)給世界上許多公司和廠商。目前采用arm技術(shù)知識(shí)產(chǎn)權(quán)內(nèi)核的微處理器,即通常所說(shuō)的arm微處理器

          所以arm也是對(duì)一類微處理器的通稱。

          arm指令集體系版本號(hào)(軟件)為V1~V7目前V1~V3已很少見(jiàn)。從V4版不再與以前的版本兼容。

          arm的CPU系列(硬件)主要有ARM7~ARM11

          2.典型的嵌入式處理器

          arm占市場(chǎng)79.5%ARM

          mips占市場(chǎng)13.9%MIPS

          microSPARC占市場(chǎng)3.1%SUN

          PowerPc占市場(chǎng)2.8%IBM

          其它占市場(chǎng)0.8%

          3.arm的應(yīng)用范圍:

          工業(yè)控制:如機(jī)床、自動(dòng)控制等

          無(wú)線通信:如手機(jī)

          網(wǎng)絡(luò)應(yīng)用:如

          電子產(chǎn)品:如音視頻播放噐、機(jī)頂盒、游戲機(jī)、數(shù)碼相機(jī)、打印機(jī)

          其它各領(lǐng)域:如軍事、醫(yī)療、機(jī)器人、智能家居

          4.計(jì)算機(jī)體系結(jié)構(gòu)

          見(jiàn)圖:馮.諾依曼計(jì)算機(jī)體系圖

          馮.諾依曼體系結(jié)構(gòu)

          處理器使用同一個(gè)存儲(chǔ)器,經(jīng)由同一個(gè)總線傳輸

          完成一條指令需要3個(gè)步驟:即取指令->指令譯碼->執(zhí)行指令

          指令和數(shù)據(jù)共享同一總線的結(jié)構(gòu)

          哈佛體系結(jié)構(gòu)

          將程序指令存儲(chǔ)和數(shù)據(jù)存儲(chǔ)分開(kāi)

          中央處理器首先到程序指令存儲(chǔ)器中讀取程序指令。解碼后到數(shù)據(jù)地址,再到相應(yīng)的數(shù)據(jù)存儲(chǔ)器讀取數(shù)據(jù),然后執(zhí)行指令

          程序指令存儲(chǔ)與數(shù)據(jù)存儲(chǔ)分開(kāi),可以使指令和數(shù)據(jù)有不同的數(shù)據(jù)寬度。

          5.復(fù)雜指令集與精簡(jiǎn)指令集

          CISC復(fù)雜指令集:采用馮.諾依曼體系結(jié)構(gòu)。數(shù)據(jù)線和指令線分時(shí)復(fù)用(只能通過(guò)一輛車)。

          存儲(chǔ)器操作指令多匯編程序相對(duì)簡(jiǎn)單指令結(jié)束后響應(yīng)中斷CPU電路豐富面積大功耗

          RISC精簡(jiǎn)指令集:采用哈佛體系結(jié)構(gòu)。數(shù)據(jù)線和指令線分離(同時(shí)能通過(guò)多輛車)。

          對(duì)存儲(chǔ)器操作有限匯編程序占空間大在適當(dāng)?shù)胤巾憫?yīng)中斷CPU電路較少體積小功耗低

          ARM采用RISC精簡(jiǎn)指令集

          Thumb是ARM體系結(jié)構(gòu)中一種16位的指令集。

          從ARMv4T之后,的ARM處理器有一種16-bit指令模式,叫做Thumb,較短的指令碼提供整體更佳的編碼密度,更有效地使用有限的內(nèi)存帶寬。所有ARM9和后來(lái)的家族,包括XScale都納入了Thumb技術(shù)。

          即ARM有兩種指令集:RISC、Thumb

          6.arm的思想

          1)arm體系的總思想:

          在不犧牲性能的同時(shí),盡量簡(jiǎn)化處理器。同時(shí)從體系結(jié)構(gòu)上靈活支持處理器擴(kuò)展。采用RISC結(jié)構(gòu)。RISC處理器簡(jiǎn)化了處理器結(jié)構(gòu),減少?gòu)?fù)雜功能指令的同時(shí),提高了處理器速度。

          ARM及MIPS都是典型的RISC處理器

          2)arm的流水線結(jié)構(gòu)

          arm處理器使用流水線來(lái)增加處理器指令流的速度,這樣可以使幾個(gè)操作同時(shí)進(jìn)行。并使處理和存儲(chǔ)器系統(tǒng)連續(xù)操作。

          arm處理器分為三級(jí):取指->譯碼->執(zhí)行

          取指:指令從存儲(chǔ)器中取出

          譯碼:對(duì)指令使用的寄存器進(jìn)行譯碼

          執(zhí)行:從寄存器組中讀取寄存器,執(zhí)行移位和ALU操作,寄存器被寫回到寄存器組中

          3)ARM處理器支持的類型

          字節(jié)8位

          半字16位

          字32位

          所有數(shù)據(jù)操作都以字為單位

          ARM指令的長(zhǎng)度剛好是一個(gè)字,Thumb指令長(zhǎng)度剛好是半個(gè)字

          4)ARM處理器狀態(tài)

          ARM處理器內(nèi)核使用ARM結(jié)構(gòu),該結(jié)構(gòu)包含32位的ARM指令集和16位Thumb指令集,因此ARM有兩種操作狀態(tài)

          ARM狀態(tài):32位

          Thumb狀態(tài):16位

          5)處理器模式

          ARM處理器共有7種運(yùn)行模式:

          用戶:正常程序工作模式,不能直接切換到其它模式

          系統(tǒng):用于支持操作系統(tǒng)的特權(quán)任務(wù),可以直接切換到其它模式

          快中斷:支持高速數(shù)據(jù)傳輸及通道處理,F(xiàn)IQ異常響應(yīng)時(shí)進(jìn)入此模式

          中斷:用于通用中斷處理,IRQ異常響應(yīng)時(shí)進(jìn)入此模式

          管理:操作系統(tǒng)保護(hù)代碼,系統(tǒng)復(fù)位和軟件中斷響應(yīng)時(shí)進(jìn)入此模式

          中止:用于支持虛擬內(nèi)存或存儲(chǔ)器保護(hù),用于MMU

          未定義:支持硬件協(xié)處理器的軟件仿真,未定義指令異常響應(yīng)時(shí)進(jìn)入此模式。

          (二)、經(jīng)典平臺(tái)硬件組成

          見(jiàn)圖:arm硬件組成圖

          開(kāi)發(fā)板一般是由一塊組成的,有核心器件和外圍器件接口等,但是有的是由兩塊板子組成,主版和核心板,主版上主要是外圍接口,外圍器件等,核心板上主要是核心器件,還有一些晶振電路等

          1.核心板(天嵌2440)

          CPU處理器S3C2440AL,主頻400MHz(最高可達(dá)533MHz)

          SDRAM內(nèi)存板載64MBSDRAM(標(biāo)準(zhǔn)配置),32bit數(shù)據(jù)總線SDRAM時(shí)鐘頻率高達(dá)100MHz(支持運(yùn)行133MHz)

          NandFlash板載64MBNandFlash或256MBNandFlash(標(biāo)準(zhǔn)配置)

          NorFlash板載2MBNorFlash(最高可升級(jí)到8MB)

          CorePower專業(yè)1.25V核心電壓供電

          Power核心板采用3.3V供電

          Powerled核心板電源指示燈

          核心板接口接口型號(hào)為DC-2.0雙列直插

          SDRAM:隨機(jī)存儲(chǔ)器,普遍使用的內(nèi)存。用作主存。

          NORFlash和NANDFlash是現(xiàn)在市場(chǎng)上兩種主要的非易失閃存。

          NOR的特點(diǎn)是芯片內(nèi)執(zhí)行,應(yīng)用程序可以直接在flash閃存內(nèi)運(yùn)行,不必再把代碼讀到系統(tǒng)RAM中。

          NAND結(jié)構(gòu)能提供極高的單元密度,可以達(dá)到高存儲(chǔ)密度,并且寫入和擦除的速度也很快。

          2.主板

          電源

          并口線

          復(fù)位

          RTC電源

          RS232電平轉(zhuǎn)換DB9插座

          音頻IIS,AC97

          按鍵、PS/2與IC接口

          數(shù)碼管

          觸摸屏

          以太網(wǎng)

          主USBHUB1轉(zhuǎn)4

          3.寄存器

          見(jiàn)圖:ARM模塊和內(nèi)核框圖

          寄存器是中央處理器內(nèi)的組成部份。

          寄存器是有限存貯容量的高速存貯部件,用來(lái)暫存指令、數(shù)據(jù)和位址。在中央處理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序計(jì)數(shù)器(PC)。在中央處理器的算術(shù)及邏輯部件中,包含的寄存器有累加器(ACC)。

          IR用于存儲(chǔ)指令

          PC用于存儲(chǔ)程序運(yùn)行的地址(即當(dāng)前指令在內(nèi)存中的位置)

          寄存器是由一個(gè)指令的輸出或輸入可以直接索引到的暫存器群組。所有的計(jì)算機(jī)指令都是進(jìn)入寄存器后被直接讀取

          ARM的匯編編程,本質(zhì)上就是針對(duì)CPU寄存器的編程。

          //*重點(diǎn)需要背訟*

          ARM寄存器分為2類:普通寄存器和狀態(tài)寄存器

          (1)通用寄存器和計(jì)數(shù)器:共32個(gè),15個(gè)通用寄存器

          R0-R7未備份寄存器

          R0(a1)R1(a1)R2(a3)R3(a4)R4(v1)R5(v2)R6(v3)R7(v4)

          R8-R12備份寄存器

          R8(v5)R9(SB,v6)R10(SL,v7)R11(EP,v8)R12(IP)數(shù)據(jù)寄存器

          R15(PC)程序計(jì)數(shù)器它的值是當(dāng)前正在執(zhí)行的指令在內(nèi)存中的位置。

          當(dāng)指令執(zhí)行結(jié)束后,CPU會(huì)自動(dòng)將PC值加上一個(gè)單位,PC值指向下一條即將執(zhí)行的指令的地址

          如果通過(guò)匯編指令對(duì)PC寄存器賦值,就會(huì)完成一次程序的跳轉(zhuǎn)(如從子函數(shù)跳轉(zhuǎn)回主函數(shù)內(nèi))

          R14(LR)鏈接寄存器存放子程序的返回地址

          例如:在主函數(shù)內(nèi),如果調(diào)用子函數(shù),程序會(huì)進(jìn)入到子函數(shù)內(nèi)執(zhí)行。當(dāng)子函數(shù)執(zhí)行完畢后,需要回到

          主函數(shù)內(nèi),所以,在子函數(shù)調(diào)用前需要將這個(gè)地址先保存起來(lái),否則無(wú)法找到這個(gè)地址。

          LR用于保存這個(gè)地址,這個(gè)地址也稱為子程序返回地址。當(dāng)子函數(shù)結(jié)束后,再將LR內(nèi)的地址賦給PC即可。

          如果子程序再調(diào)用孫程序,LR如何保存地址呢?

          先把當(dāng)前LR內(nèi)的值壓入內(nèi)存的棧區(qū),然后LR再保存孫程序的返回地址。當(dāng)孫程序執(zhí)行完后通過(guò)PC跳轉(zhuǎn)到

          子程序內(nèi),此時(shí)將棧區(qū)內(nèi)的子程序返回地址取出保存在LR內(nèi)。當(dāng)子程序執(zhí)行完后,再通過(guò)PC跳轉(zhuǎn)到主函數(shù)內(nèi)。

          R13(SP)棧指針寄存器用于存放堆棧的棧頂?shù)刂贰?/p>

          SP相當(dāng)于指針變量,保存的是棧頂?shù)牡刂?,出棧時(shí),從SP指向的內(nèi)存中取出數(shù)據(jù),入棧時(shí)將新的內(nèi)存地址

          壓入棧頂,而SP相當(dāng)于鏈表的頭指針(head)。

          原則上說(shuō)R0-R12可以保存任何數(shù)據(jù)。其中R0-R7用來(lái)臨時(shí)存儲(chǔ)數(shù)據(jù),R8-R12系統(tǒng)沒(méi)有用來(lái)做任何特殊用途,常用于中斷

          而在匯編與C語(yǔ)言的交互中,定制了ATPCS標(biāo)準(zhǔn)

          寄存器:R4-R11用來(lái)保存局部變量

          參數(shù):參數(shù)小于等于4,用R0-R3保存參數(shù),參數(shù)多于4,剩余的傳入堆棧

          函數(shù)返回:結(jié)果為32位整數(shù),通過(guò)R0返回

          結(jié)果為64位整數(shù),通過(guò)R0,R1返回

          對(duì)于位數(shù)更多的結(jié)果,通過(guò)內(nèi)存?zhèn)鬟f

          (2)狀態(tài)寄存器:

          狀態(tài)寄存器用于保存程序的當(dāng)前狀態(tài)

          CPSR當(dāng)前程序狀態(tài)寄存器

          一個(gè)寄存器為32位,每一位數(shù)據(jù)代表不同的狀態(tài)。分為三個(gè)部分(條件代碼標(biāo)志位、控制位、保留區(qū)位)

          31322928....76543210

          NZCVIFTM4M3M2M1M0

          其中NZCV稱為條件標(biāo)志位(即保存的是條件的運(yùn)算結(jié)果,真和假)

          N=1表示運(yùn)算結(jié)果為負(fù)數(shù),N=0表示運(yùn)算結(jié)果為正數(shù)。

          Z=1表示運(yùn)算結(jié)果為0,Z=0表示運(yùn)算結(jié)果為非零。

          C=1表示運(yùn)算結(jié)果產(chǎn)生了進(jìn)位。

          V=1運(yùn)算結(jié)果的符號(hào)位發(fā)生了溢出。

          這4個(gè)位的組合,代表了各種條件,如下:

          0000 EQ Z置位相等/等于0

          0001 NE Z清0不等

          0010 CS/HS C置位進(jìn)位/無(wú)符號(hào)高于或等于

          0011 CC/LO C清0無(wú)進(jìn)位/無(wú)符號(hào)低于

          0100 MI N置位負(fù)數(shù)

          0101 PL N清0非負(fù)數(shù)

          0110 VS V置位溢出

          0111 VC V清0無(wú)溢出

          1000 HI C置位且Z清0無(wú)符號(hào)高于

          1001 LS C清0或Z置位無(wú)符號(hào)低于或等于

          1010 GE N等于V有符號(hào)大于或等于

          1011 LT N不等于V有符號(hào)小于

          1100 GT Z清0且N等于V有符號(hào)大于

          1101 LE Z置位或N不等于V有符號(hào)小于或等于

          1110 AL 任何狀態(tài) 總是(always)

          1111 NV 無(wú) 從不(never)

          IFT稱為控制位

          II=1禁用IRO中斷

          FF=1禁用FIQ中斷

          T表示CPU當(dāng)前的狀態(tài),1代表正在Thumb指令集狀態(tài),0表示正在ARM指令集狀態(tài)。

          M0至M4表示中斷類型(控制位內(nèi)的模式位)

          0b10000User用戶中斷

          0b10001FIQ快速中斷

          0b10010IRQ聲卡、調(diào)制解調(diào)器等外部設(shè)備產(chǎn)生的中斷

          0b10011Supervisor管理程序或監(jiān)控程序產(chǎn)生的中斷

          0b10111Abort異常中斷

          0b11011Undefined未定義中斷

          0b11111System系統(tǒng)中斷

          SPSR保存的程序狀態(tài)寄存器,結(jié)構(gòu)與CPSR完全一樣,用來(lái)保存CPSR的值。以便出現(xiàn)異常時(shí)恢復(fù)CPSR的值

          (3)流水線對(duì)pc值的影響

          CPU內(nèi)部的組成部分:指令寄存器,指令譯碼器,指令執(zhí)行單元(包括ALU和通用寄存器組)

          CPU執(zhí)行指令的步驟:取指->譯碼->執(zhí)行

          取指:將指令從內(nèi)存或指令cache中取入指令寄存器

          譯碼:指令譯碼器對(duì)指令寄存器中的指令進(jìn)行譯碼操作,辨識(shí)add,或是sub等操作

          執(zhí)行:指令執(zhí)行單元根據(jù)譯碼的結(jié)果進(jìn)行運(yùn)算并保存結(jié)果

          流水線操作:并發(fā)多條流水線(以3條為例)

          1取指譯碼執(zhí)行

          2取指譯碼執(zhí)行

          3取指譯碼執(zhí)行

          提高時(shí)間效率

          (三)、學(xué)習(xí)內(nèi)容

          1.匯編(對(duì)裸板機(jī)的控制,以及驅(qū)動(dòng)程序控制)

          2.內(nèi)核移植(uboot移植、內(nèi)核編譯、文件系統(tǒng)移植、應(yīng)用程序移植)

          3.驅(qū)動(dòng)程序編寫

          //^^^^^^^^^^^^^^^^^^^^^^^^^^下午^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

          (四)、ADS的使用

          ADS是匯編或C語(yǔ)言編譯調(diào)試工具

          可生成的文件:.axf含調(diào)試信息的可執(zhí)行ELF文件

          .bin可燒寫的二進(jìn)制映像文件

          .hex可燒寫的十六進(jìn)制映像文件

          ADS配置

          在磁盤中新建一個(gè)目錄D:arm,用來(lái)存儲(chǔ)所寫的代碼

          點(diǎn)擊目錄:File->new,創(chuàng)建一個(gè)可執(zhí)行的ARM映象工程

          ARMExecutableImage生成ELF格式映像(bin)

          ARMObjectLibrary生成armar格式目標(biāo)庫(kù)文件

          EmptyProject創(chuàng)建不包含任何庫(kù)或源文件的工程

          MakefileImporterWizard用于Vc

          ThumbARMInterworkingImage用于ARM和thumb指令混合代碼生成的ELF映像

          ThumbExecutableimage用thumb指令生成ELF格式映像

          ThumbObjectLibrary用于Thumb指令的代碼生成的armar格式文件

          選ARMExecutableImage,工程文件名2440ART

          加源文件

          project->AddFiles

          新建

          填加已有

          生成目標(biāo)的配置

          2440ART.mcp內(nèi)雙擊

          TargetSettins:post-linker選擇ArMfromELF

          LanguageSettins:ArchitectureorProcessor選擇相應(yīng)的編譯器ARM920T

          ArmLinker:output內(nèi)RO0x30000000

          options內(nèi)Imageentrypoint設(shè)為0x30000000

          layout內(nèi)Object2440init.oSectionInit

          Listings內(nèi)Imagemap

          ArmfromELF:outputformat內(nèi)Plainbinary

          outputfilename內(nèi)*.bin

          編譯

          make

          調(diào)試AXD是調(diào)試器

          設(shè)置,debug->打開(kāi)AXD調(diào)試界面,選擇option->configtarget選項(xiàng)

          選ARMUL(模擬調(diào)試器),然后選擇確定.進(jìn)入調(diào)試界面.

          ARMUL是虛擬調(diào)試環(huán)境(虛擬開(kāi)發(fā)板)

          如果用開(kāi)發(fā)板真實(shí)環(huán)境調(diào)試,則需要使用JTAG連開(kāi)發(fā)板后,在此處選H-JTAG

          用file-

          execute->runtocousor項(xiàng).使程序進(jìn)入用戶主程序

          可以用F8來(lái)一條一條執(zhí)行語(yǔ)句,也可用F10,可以設(shè)置斷點(diǎn).

          (五).匯編語(yǔ)言基本結(jié)構(gòu)

          例:

          AREAInit,CODE,READONLY;AREA定義代碼段,段名Init;代碼段,只讀

          ENTRY;偽操作,第一條指令的入口

          Start;標(biāo)號(hào),一段代碼的開(kāi)始,用于標(biāo)記,無(wú)意義,必須頂格

          MOVr0,#10;將10存入r0寄存器,整型常量前面用#號(hào)

          MOVr1,#3;將3存入r1寄存器,r0和r1相當(dāng)于兩個(gè)變量,只是名稱固定,在寄存器的存儲(chǔ)空間內(nèi)

          ADDr0,r0,r1;將r0內(nèi)數(shù)據(jù)與r1內(nèi)數(shù)據(jù)相加,相加后數(shù)據(jù)放在r0內(nèi)

          ;Stop;停止標(biāo)號(hào),下面的代碼用于停止運(yùn)行程序

          ;MOVr0,#0x18;軟件異常中斷響應(yīng)

          ;LDRr1,=0x20026;ADP停止運(yùn)行,應(yīng)用退出

          ;SWI0x123456;ARM半主機(jī)軟件中斷

          END

          1).基本概念

          (2)寄存器:如R0、R1等

          ARM的匯編編程,本質(zhì)上就是針對(duì)CPU寄存器的編程。

          (3)指令:即操作碼,直接控制CPU,如MOV

          包括跳轉(zhuǎn)指令、數(shù)據(jù)處理指令、乘法指令、PSR訪問(wèn)指令、加載或存儲(chǔ)指令、數(shù)據(jù)交換指令、移位指令等

          (4)偽操作:作用于編譯器,大多用于定義和控制。如AREA

          包括符號(hào)定義、數(shù)據(jù)定義、控制等

          (5)標(biāo)號(hào):僅是一種標(biāo)識(shí)。在跳轉(zhuǎn)語(yǔ)句中,可以指向要跳轉(zhuǎn)到的標(biāo)識(shí)號(hào)位置

          在ARM匯編中,標(biāo)號(hào)代表一個(gè)地址,段內(nèi)標(biāo)號(hào)的地址在匯編時(shí)確定,段外標(biāo)號(hào)的地址值在連接時(shí)確定

          (6)符號(hào):即標(biāo)號(hào)(代表地址)、變量名、數(shù)字常量名等。符號(hào)的命名規(guī)則如下:

          a.符號(hào)由大小寫字母、數(shù)字以及下劃線組成;

          b.除局部標(biāo)號(hào)以數(shù)字開(kāi)頭外,其它的符號(hào)不能以數(shù)字開(kāi)頭;

          c.符號(hào)區(qū)分大小寫,且所有字符都是有意義的;

          d.符號(hào)在其作用域范圍你必須是唯一的;

          e.符號(hào)不能與系統(tǒng)內(nèi)部或系統(tǒng)預(yù)定義的符號(hào)同名;

          f.符號(hào)不要與指令助記符、偽指令同名。

          2).段定義

          在匯編語(yǔ)言中,以相對(duì)獨(dú)立的指令或數(shù)據(jù)序列的程序段組成程序代碼

          段的劃分:數(shù)據(jù)段、代碼段。一個(gè)匯編程序至少有一個(gè)代碼段

          (1)代碼段

          上面的例子為代碼段。

          AREA定義一個(gè)段,并說(shuō)明所定義段的相關(guān)屬性,CODE用以指明為代碼段

          ENTRY標(biāo)識(shí)程序的入口點(diǎn)。END為程序結(jié)束。

          (2)數(shù)據(jù)段

          AREADATAAREA,DATA,BIINIT,ALLGN=2

          DISPBUFSPACE200

          RCVBUFSPACE200

          DATA用以指明為數(shù)據(jù)段,

          SPACE分配200字節(jié)的存儲(chǔ)單元并初始化為0

          指令和偽操作在后面詳細(xì)描述

          3).匯編語(yǔ)言結(jié)構(gòu)

          [標(biāo)號(hào)][指令或偽操作]

          所有標(biāo)號(hào)必須在一行的頂格書寫,其后不加冒號(hào)

          所有指令均不能頂格寫

          指令助記符大小寫敏感,不能大小寫混合,只能全部大寫或全部小寫

          ;為注釋

          @代碼行注釋,同;

          #整行注釋或直接操作數(shù)前綴

          為換行符

          ENTRY為程序的入口

          END為程序的結(jié)束

          //*arm體系結(jié)構(gòu)第二天*

          二、ARM的尋址方式

          最簡(jiǎn)單的匯編指令格式是操作碼和操作數(shù)

          如:MOVr0,#10

          操作碼:即CPU指令如MOVADD

          操作數(shù):即表示數(shù)據(jù)是在寄存器中還是在內(nèi)存中,是絕對(duì)地址還是相對(duì)地址

          操作數(shù)部分要解決的問(wèn)題是,到哪里去獲取操作數(shù),獲取操作數(shù)的方式就是尋址方式。

          ARM每一條指令都是32位機(jī)器碼,對(duì)應(yīng)CPU的位數(shù)

          ARM指令格式:

          在32位中分為7個(gè)位域,每個(gè)位域分別存儲(chǔ)不同意義的編碼(二進(jìn)制數(shù)

          31~2827~2524~212019~1615~1211~0

          -----------------------------------------------------------

          |Cond|001|Opcode|S|Rn|Rd|Operand2|

          -----------------------------------------------------------

          對(duì)應(yīng)輸入的一條指令為:

          {}{s},,

          Opcode:指令操作碼用4個(gè)位存儲(chǔ),如MOV,ADD每一個(gè)操作碼和操作數(shù)最終都是以二進(jìn)制形式存在

          Cond:指令的條件碼用4個(gè)位來(lái)儲(chǔ),可省略

          如:ADDr0,r0,#1;計(jì)算r0加1后的值,再寫入到r0內(nèi)

          ADDEQr0,r0,#1;只有在CPSR寄存器條件標(biāo)志位滿足指定條件時(shí),才計(jì)算r0加1后的值,再寫入到r0內(nèi)

          其中條件助記符如下:

          EQ相等

          NE不相等

          MI負(fù)數(shù)

          VS溢出

          PL正數(shù)或零

          HI無(wú)符號(hào)大于

          LS無(wú)符號(hào)小于或等于

          CS無(wú)符號(hào)大于或等于

          CC無(wú)符號(hào)小于

          GT有符號(hào)大于

          GE有符號(hào)大于或等于

          LE有符號(hào)小于或等于

          LT有符號(hào)小于

          AL無(wú)條件執(zhí)行

          S:決定指令的操作是否影響CPSR的值,用一個(gè)位存儲(chǔ),省略則表示為0值,否則為1值

          如SUBSR0,R0,#1;R0減1,結(jié)果放入R0,同時(shí)響影CPSR的值

          SUBR0,R0,#1;R0減1,結(jié)果放入R0,不影響CPSR的值

          Rd:目標(biāo)寄存器編碼用4位存儲(chǔ)

          Rn:第1個(gè)操作數(shù)的寄存器編碼用4位存儲(chǔ)

          Operand2:第2個(gè)操作數(shù)用12位存儲(chǔ)

          尋址方式:是根據(jù)指令中給出的地址碼字段來(lái)實(shí)現(xiàn)尋找真實(shí)操作數(shù)地址的方式,共8種尋址方式:

          寄存器尋址、立即尋址、寄存器間接尋址、基址尋址、多寄存器尋址、堆棧尋址、相對(duì)尋址、寄存器移位尋址

          //*尋址方式是重點(diǎn)需要理解背訟*

          1.立即尋址

          操作數(shù)是常量,用#表示常量。例

          MOVR0,#0xFF000;指令省略了第1個(gè)操作數(shù)寄存器。將立即數(shù)0xFF000(第2操作數(shù))裝入R0寄存器

          SUBR0,R0,#64;R0減64,結(jié)果放入R0

          #表示立即數(shù)0x或&表示16進(jìn)制數(shù)否則表示十進(jìn)制數(shù)

          立即數(shù)尋址指令中的地址碼就是操作數(shù)本身,可以立即使用的操作數(shù)。

          其中,#0xFF000和#64都是立即數(shù)。該立即數(shù)位于32位機(jī)器碼中,占低位的12位。也就是說(shuō)在ARM指令中以12位存儲(chǔ)一個(gè)數(shù)據(jù)

          那么,32位的數(shù)據(jù)(long或float)如何存儲(chǔ)?

          32位的數(shù)據(jù)以一種特殊的方式來(lái)處理

          其中:高4位表示的無(wú)符號(hào)整數(shù)

          低8位補(bǔ)0擴(kuò)展為32位,然后循環(huán)右移x位來(lái)代表一個(gè)數(shù)。x=高4位整數(shù)*2

          所以,不是每一個(gè)32位數(shù)都是合法的立即數(shù),只有能通過(guò)上述構(gòu)造得到的才是合法的立好數(shù)。如:

          合法立即數(shù):0xff,0x104,0xff0

          不合法立即數(shù):ox101,0x102,0xff1

          2.寄存器尋址

          操作數(shù)的值在寄存器中,指令執(zhí)行時(shí)直接取出寄存器值來(lái)操作

          例:MOVR1,R2;將R2的值存入R1在第1個(gè)操作數(shù)寄存器的位置存放R2編碼

          SUBR0,R1,R2;將R1的值減去R2的值,結(jié)果保存到R0在第2操作數(shù)位置,存放的是寄存器R2的編碼

          寄存器尋址是根據(jù)寄存器編碼獲取寄存器內(nèi)存儲(chǔ)的操作數(shù)

          3.寄存器間接尋址

          操作數(shù)從寄存器所指向的內(nèi)存中取出,寄存地存儲(chǔ)的是內(nèi)存地址

          例:LDRR1,[R2];將R2指向的存儲(chǔ)單元的數(shù)據(jù)讀出,保存在R1中R2相當(dāng)于指針變量

          STRR1,[R2];將R1的值寫入到R2所指向的內(nèi)存

          SWPR1,R1,[R2];將寄存器R1的值和R2指定的存儲(chǔ)單元的內(nèi)容交換

          [R2]表示寄存器所指向的內(nèi)存,相當(dāng)于*p

          LDR指令用于讀取內(nèi)存數(shù)據(jù)

          STR指令用于寫入內(nèi)存數(shù)據(jù)

          //-----------------------------------------------------

          4.寄存器基址尋址

          操作數(shù)從內(nèi)存址偏移后讀取數(shù)據(jù)。常用于查表、數(shù)組操作、功能部件寄存器訪問(wèn)等。

          基址:相當(dāng)于首地址,地址偏移后取值作為操作數(shù)

          基址寄存器:用來(lái)存放基址的寄存器

          變址尋址:基址尋址也稱為變址尋址

          1)前索引尋址

          例:LDRR2,[R3,#4];讀取R3+4地址上的存儲(chǔ)單元的內(nèi)容,放入R2

          LDRR2,[R3,#4]!;讀取R3+4地址上的存儲(chǔ)單元的內(nèi)容,放入R2,然后R3內(nèi)的地址變?yōu)镽3+4,即R3=R3+4

          [R3,#4]表示地址偏移后取值,相當(dāng)于*(p+4)或p[4],R3內(nèi)保存的地址不變

          [R3,#4]!表示地址偏移后取值,相當(dāng)于*(p+4)或p[4],!表示回寫,即R3=R3-4,R3的值發(fā)生了改變

          2)后索引尋址

          例:LDRR2,[R3],#4;先讀取R3地址上的存儲(chǔ)單元的內(nèi)容,放入R2,然后R3內(nèi)的地址變?yōu)镽3+4,即R3=R3+4

          [R3],#4類似于*p++只不過(guò)自加的不是1,而是指定的4

          [R3,#4]!類似于*++p只不過(guò)自加的不是1,而是指定的4

          3)寄存器的值作索引

          例:LDRR2,[R3,R0];R0內(nèi)存儲(chǔ)索引值,R3內(nèi)存地址,讀取R3+R0地址上的存儲(chǔ)單元的內(nèi)容,放入R2

          [R3,R0]表示地址偏移后取值,相當(dāng)于*(p+i)或p[i]

          5.多寄存器尋址

          一次可傳送多個(gè)寄存器的值,也稱為塊拷貝尋址

          例:LDMIAR1!,{R2-R7,R12};將R1指向的存儲(chǔ)單元中的數(shù)據(jù)讀寫到R2~R7、R12中,然后R1自加1

          STMIAR1!,{R2-R7,R12};將寄存器R2~R7、R12的值保存到R1指向的存儲(chǔ)單元中,然后R1自加1

          其中R1是基址寄存器,用來(lái)存基址,R2-R7、R12用來(lái)存數(shù)據(jù)賦值編號(hào)小的寄存器與低地址相對(duì)應(yīng),與寄存器列表順序無(wú)關(guān)

          !為可選后綴,表示改變R1的值,則當(dāng)數(shù)據(jù)傳送完畢之后,將最后的地址寫入基址寄存器

          基址寄存器不允許為R15,寄存器列表可以為R0~R15的任意組合。

          這里R1沒(méi)有寫成[R1]!,是因?yàn)檫@個(gè)位不是操作數(shù)位,而是寄存器位

          LDMIA和STMIA是塊拷貝指令,LDMIA是從R1所指向的內(nèi)存中讀數(shù)據(jù),STMIA是向R1所指向的內(nèi)存寫入數(shù)據(jù)

          R1指向的是連續(xù)地址空間

          6.寄存器堆棧尋址

          是按特定順序存取存儲(chǔ)區(qū),按后進(jìn)先出原則,使用專門的寄存器SP(堆棧指針)指向一塊存儲(chǔ)區(qū)

          例:LDMIASP!,{R2-R7,R12};將棧內(nèi)的數(shù)據(jù),讀寫到R2~R7、R12中,然后下一個(gè)地址成為棧頂

          STMIASP!,{R2-R7,R12};將寄存器R2~R7、R12的值保存到SP指向的棧中

          SP指向的是棧頂

          7.相對(duì)尋址

          即讀取指令本身在內(nèi)存中的地址。是相對(duì)于PC內(nèi)指令地址偏移后的地址。

          由程序計(jì)數(shù)器PC提供基準(zhǔn)地址,指令中的地址碼字段作為偏移量,兩者相加后得到的地址即為操作數(shù)的有效地址。

          例:

          BLOOP;B指令用于跳轉(zhuǎn)到標(biāo)號(hào)LOOP指令處執(zhí)行代碼

          ...

          LOOP

          MOVR6#1

          其中LOOP僅僅是標(biāo)號(hào),而不是地址,不是CPU指令,所以在指令的機(jī)器碼中沒(méi)有標(biāo)號(hào)的機(jī)器碼,而是

          計(jì)算出了從B指令到MOV指令之間內(nèi)存地址的差值,這個(gè)差值相當(dāng)于PC的偏移量,即相當(dāng)于:addpc,pc,#偏移量

          B指令引起了PC寄存器值的變化,PC內(nèi)永遠(yuǎn)保存將要運(yùn)行指令在內(nèi)存中的地址。

          8.寄存器移位尋址

          操作數(shù)在被使用前進(jìn)行移位運(yùn)算

          例:MOVR0,R2,LSL#3;R2的值左移3位,結(jié)果放入R0,;即是R0=R2×8

          LSL是移位指令,用于將前面寄存器的數(shù)據(jù)左移

          //^^^^^^^^^^^^^^^^^^^^^^^^^^下午^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

          ARM匯編語(yǔ)言語(yǔ)句由指令、偽指令、偽操作、宏指令組成

          三.ARM指令集

          共包括6種類型:數(shù)據(jù)處理指令、跳轉(zhuǎn)指令、程序狀態(tài)指令、加載存取儲(chǔ)指令、協(xié)處理指令、軟中斷指令

          (一)、數(shù)據(jù)處理指令

          數(shù)據(jù)處理指令,只能對(duì)寄存器內(nèi)容進(jìn)行操作,而不能對(duì)內(nèi)存進(jìn)行操作,所有數(shù)據(jù)處理指令均可使用S后綴,并影響狀態(tài)標(biāo)志

          包括:數(shù)據(jù)傳送指令、算述指令、乘法指令、邏輯運(yùn)算指令、比較指令、移位指令

          1.數(shù)據(jù)傳輸指令

          1)MOV數(shù)據(jù)傳送指令

          格式:MOVRd,

          功能:Rd=操作數(shù),操作數(shù)可以是寄存器、被移位的寄存器或立即數(shù)。

          例:

          MOVR0,#0xFF000;立即尋址,將立即數(shù)0xFF000(第2操作數(shù))裝入R0寄存器

          MOVR1,R2;寄存器尋址,將R2的值存入R1

          MOVR0,R2,LSL#3;移位尋址,R2的值左移3位,結(jié)果放入R0

          2)MVN數(shù)據(jù)取反傳送指令

          格式:MVN,;

          功能:將操作數(shù)傳送到目的寄存器Rd中,但該值在傳送前被按位取反,即Rd=!op1;

          例:

          MVNR0,#0;R0=-1

          mvn意為“取反傳輸”,它把源寄存器的每一位取反,將得到的結(jié)果寫入結(jié)果寄存器。

          movs和mvns指令對(duì)pc寄存器賦值時(shí)有特殊含義,表示要求在賦值的同時(shí)從spsr中恢復(fù)cpsr。

          mov和mvn指令,編譯器會(huì)進(jìn)行智能的轉(zhuǎn)化。比如指令“movr1,0xffffff00”中的立即數(shù)是非法的。

          在編譯時(shí),編譯器將其轉(zhuǎn)化為“mvnr1,0xff”,這樣就不違背立即數(shù)的要求。所以對(duì)于mov和mvn指令,

          可以認(rèn)為:合法的立即數(shù)反碼也是合法的立即數(shù)。

          2.算術(shù)指令

          1)ADD加法指令

          格式:ADD{}{S},,;

          功能:Rd=Rn+op2

          例:

          ADDR0,R1,#5;R0=R1+5

          ADDR0,R1,R2;R0=R1+R2

          ADDR0,R1,R2,LSL#5;R0=R1+R2左移5位

          2)ADC帶進(jìn)位加法指令

          格式:ADC{}{S},,;

          功能:Rd=Rn+op2+carry,carry為進(jìn)位標(biāo)志值。該指令用于實(shí)現(xiàn)超過(guò)32位的數(shù)的加法。

          例如:

          第一個(gè)64位操作數(shù)存放在寄存器R2,R3中;

          第二個(gè)64位操作數(shù)存放在寄存器R4,R5中;

          64位結(jié)果存放在R0,R1中。

          ADDSR0,R2,R4;低32位相加,S表示結(jié)果影響條件標(biāo)志位的值

          ADCR1,R3,R5;高32位相加

          3)SUB減法指令

          格式:SUB{}{S},,;

          功能:Rd=Rn-op2

          例:

          SUBR0,R1,#5;R0=R1-5

          SUBR0,R1,R2;R0=R1-R2

          SUBR0,R1,R2,LSL#5;R0=R1-R2左移5位

          4)SBC帶借位減法指令

          格式:SBC{}{S},,;

          功能:Rd=Rn-op2-!carry

          SUB和SBC生成進(jìn)位標(biāo)志的方式不同于常規(guī),如果需要借位則清除進(jìn)位標(biāo)志,所以指令要對(duì)進(jìn)位標(biāo)志進(jìn)行一個(gè)非操作。

          例:

          第一個(gè)64位操作數(shù)存放在寄存器R2,R3中;

          第二個(gè)64位操作數(shù)存放在寄存器R4,R5中;

          64位結(jié)果存放在R0,R1中。

          SUBSR0,R2,R4;低32位相減,S表示結(jié)果影響條件標(biāo)志位的值

          SBCR1,R3,R5;高32位相減

          5)其它減法指令

          RSB反向減法指令,同SUB指令,但倒換了兩操作數(shù)的前后位置,即Rd=op2-Rn。

          RSC帶借位的反向減法指令,同SBC指令,但倒換了兩操作數(shù)的前后位置,即Rd=op2-Rn-!carry。

          rsb r0,r1,r2 /*r0=r2–r1*/

          rsc r0,r1,r2 /*r0=r2–r1+carry–1*/

          adds和adcs在進(jìn)位時(shí)將cpsr的C標(biāo)志置1;否則置0。

          subs和sbcs在產(chǎn)生借位時(shí)將cpsr的C標(biāo)志置0;否則置1。

          3.乘法指令

          1)MUL32位乘法指令

          格式:MUL{}{S},,;

          功能:Rd=Rn×op2

          該指令根據(jù)S標(biāo)志,決定操作是否影響CPSR的值;其中op2必須為寄存器。Rn和op2的值為32位的有符號(hào)數(shù)或無(wú)符號(hào)數(shù)。

          例:

          MULSR0,R1,R2;R0=R1×R2,結(jié)果影響寄存器CPSR的值

          2)MLA32位乘加指令

          格式:MLA{}{S},,,;

          功能:Rd=Rn×op2+op3op2和op3必須為寄存器。Rn、op2和op3的值為32位的有符號(hào)數(shù)或無(wú)符號(hào)數(shù)。

          例:

          MLAR0,R1,R2,R3;R0=R1×R2+R3

          3)SMULL64位有符號(hào)數(shù)乘法指令

          格式:SMULL{}{S},,,;

          功能:RdhRdl=Rn×op2Rdh、Rdl和op2均為寄存器。Rn和op2的值為32位的有符號(hào)數(shù)。

          例:

          SMULLR0,R1,R2,R3;R0=R2×R3的低32位R1=R2×R3的高32位

          4)SMLAL64位有符號(hào)數(shù)乘加指令

          格式:SMLAL{}{S},,,;

          功能:RdhRdl=Rn×op2+RdhRdl;Rdh、Rdl和op2均為寄存器。Rn和op2的值為32位的有符號(hào)數(shù),RdhRdl的值為64位的加數(shù)。

          例:

          SMLALR0,R1,R2,R3;R0=R2×R3的低32位+R0R1=R2×R3的高32位+R1

          5)UMULL64位無(wú)符號(hào)數(shù)乘法指令

          格式:UMULL{}{S},,,;

          功能:同SMULL指令,但指令中Rn和op2的值為32位的無(wú)符號(hào)數(shù)。

          例:

          UMULLR0,R1,R2,R3;R0=R2×R3的低32位R1=R2×R3的高32位其中R2,R3的值為無(wú)符號(hào)數(shù)

          6)UMLAL64位無(wú)符號(hào)數(shù)乘加指令

          格式:UMLAL{}{S},,,;

          功能:同SMLAL指令,但指令中Rn,op2的值為32位的無(wú)符號(hào)數(shù),RdhRdl的值為64位無(wú)符號(hào)數(shù)。

          例:

          UMLALR0,R1,R2,R3;R0=R2×R3的低32位+R0R1=R2×R3的高32位+R1

          ;其中R2,R3的值為32位無(wú)符號(hào)數(shù)R1,R0的值為64位無(wú)符號(hào)數(shù)

          4.邏輯指令:and、orr、eor和bic

          1)AND邏輯與指令

          格式:AND{}{S},,;

          功能:Rd=RnANDop2一般用于清除Rn的特定幾位。

          例:

          ANDR0,R0,#5;保持R0的第0位和第2位,其余位清0

          2)ORR邏輯或指令

          格式:ORR{}{S},,;

          功能:Rd=RnORop2一般用于設(shè)置Rn的特定幾位。

          例:

          ORRR0,R0,#5;R0的第0位和第2位設(shè)置為1,其余位不變

          3)EOR邏輯異或指令

          格式:EOR{}{S},,;

          功能:Rd=RnEORop2一般用于將Rn的特定幾位取反。

          例:

          EORR0,R0,#5;R0的第0位和第2位取反,其余位不變

          4)BIC位清除指令

          格式:BIC{}{S},,;

          功能:Rd=RnAND(!op2)用于清除寄存器Rn中的某些位,并把結(jié)果存放到目的寄存器Rd中

          例:

          BICR0,R0,#5;R0中第0位和第2位清0,其余位不變

          5.比較指令

          1)CMP比較指令

          格式:CMP{},;

          功能:Rn-op1,根據(jù)結(jié)果更新CPSR中條件標(biāo)志位的值。

          例:

          CMPR0,#5;計(jì)算R0-5,根據(jù)結(jié)果設(shè)置條件標(biāo)志位

          ADDGTR0,R0,#5;ADD為加法指令,GT為判斷條件標(biāo)志位是否大于5,如果R0>5,則執(zhí)行ADD指令

          2)CMN反值比較指令

          格式:CMN{},;

          功能:同CMP指令,但寄存器Rn的值是和op1取負(fù)的值進(jìn)行比較。

          例:

          CMNR0,#5;把R0與-5進(jìn)行比較

          3)TST位測(cè)試指令

          格式:TST{},;

          功能:RnANDop1按位與后,更新CPSR中條件標(biāo)志位,用于檢查寄存器Rn是否設(shè)置了op1中相應(yīng)的位。

          例:

          TSTR0,#5;測(cè)試R0中第0位和第2位是否為1

          4)TEQ相等測(cè)試指令

          格式:TEQ{},;

          功能:RnEORop1按位異或后,更新CPSR中條件標(biāo)志位,用于檢查寄存器Rn的值是否和op1所表示的值相等。

          例:

          TEQR0,#5;判斷R0的值是否和5相等

          6.移位指令(位運(yùn)算指令)

          1)LSL(或ASL)左移

          格式:寄存器,LSL(或ASL)操作數(shù)

          功能:將寄存器內(nèi)的數(shù)據(jù)左移,操作數(shù)是移位的位數(shù)在0-31之間

          例:

          MOVR0,R1,LSL#2;將R1中的內(nèi)容左移兩位后傳送到R0中。

          2)LSR操作右移

          格式:寄存器LSR操作數(shù)

          功能:將寄存囂內(nèi)的數(shù)據(jù)右移

          例:

          MOVR0,R1,LSR#2;將R1中的內(nèi)容右移兩位后傳送到R0中,左端用零來(lái)填充。

          3)其它移位

          ASR右移,左端用第31位值來(lái)填充

          ROR右移,循環(huán)右移,左端用右端移出的位來(lái)填充

          RRX右移,循環(huán)右移,左端用進(jìn)位標(biāo)志位C來(lái)填充

          //*arm體系結(jié)構(gòu)第三天*

          (二)、跳轉(zhuǎn)指令

          1)B跳轉(zhuǎn)指令

          格式:B{};

          功能:跳轉(zhuǎn)到addr地址。

          例:

          Bexit;程序跳轉(zhuǎn)到標(biāo)號(hào)exit處

          2)BL帶返回的跳轉(zhuǎn)指令

          格式:BL{};

          功能:同B指令,但BL指令執(zhí)行跳轉(zhuǎn)操作的同時(shí),還將PC(寄存器R15)的值保存到LR寄存器(寄存器R14)中。

          該指令用于實(shí)現(xiàn)子程序調(diào)用,程序的返回可通過(guò)把LR寄存器的值到PC寄存器中來(lái)實(shí)現(xiàn)。

          例:

          BLfunc;調(diào)用子程序func

          func

          MOVR15,R14;子程序返回

          3)其它跳轉(zhuǎn)指令

          BLX帶返回和狀態(tài)切換的跳轉(zhuǎn)指令,用于子程序調(diào)用和程序Thumb狀態(tài)的切換。

          BX帶狀態(tài)切換的跳轉(zhuǎn)指令,處理器跳轉(zhuǎn)到目標(biāo)地址處,目標(biāo)地址處的指令可以是ARM指令,也可以是Thumb指令。

          跳轉(zhuǎn)指令用于實(shí)現(xiàn)程序的跳轉(zhuǎn)和程序狀態(tài)的切換。

          ARM程序設(shè)計(jì)中,實(shí)現(xiàn)程序跳轉(zhuǎn)有兩種方式:跳轉(zhuǎn)指令、直接向程序寄存器PC中寫入目標(biāo)地址值。

          //----------------------------------------------------------------------------------------

          (三)、程序狀態(tài)指令

          用于狀態(tài)寄存器和通用寄存器間傳送數(shù)據(jù)??偣灿袃蓷l指令:MRS和MSR。兩者結(jié)合可用來(lái)修改程序狀態(tài)寄存器的值。

          1)MRS程序狀態(tài)寄存器到通用寄存器的數(shù)據(jù)傳送指令

          格式:MRS{},CPSR/SPSR;

          功能:用于將程序狀態(tài)寄存器的內(nèi)容傳送到目標(biāo)寄存器Rd中。

          例:

          MRSR0,CPSR;狀態(tài)寄存器CPSR的值存入寄存器R0中

          2)MSR通用寄存器到程序狀態(tài)寄存器的數(shù)據(jù)傳送指令

          格式:MSR{}CPSR/SPSR_,;

          功能:用于將寄存器Rd的值傳送到程序狀態(tài)寄存器中。

          :用來(lái)設(shè)置狀態(tài)寄存器中需要操作的位。

          32位的狀態(tài)寄存器可以分為4個(gè)域:

          位[31:24]為條件標(biāo)志位域,用f表示。

          位[23:16]為狀態(tài)位域,用s表示。

          位[15:8]為擴(kuò)展位域,用x表示。

          位[7:0]為控制位域,用c表示。

          例:

          MSRCPSR_f,R0;用R0的值修改CPSR的條件標(biāo)志域

          MSRCPSR_fsxc,#5;CPSR的值修改為5

          (四)、加載存儲(chǔ)指令

          該集合的指令使用頻繁,當(dāng)數(shù)據(jù)存放在內(nèi)存中時(shí),必須先把數(shù)據(jù)從內(nèi)存裝載到寄存器,執(zhí)行完后再把寄存器

          中的數(shù)據(jù)存儲(chǔ)到內(nèi)存中

          1.單數(shù)據(jù)訪存指令

          1)單數(shù)據(jù)加載指令

          格式:LDR(或LDRB、LDRBT、LDRH、LDRSB、LDRSH、LDRT、STR、STRB、STRBT、STRH、STRT),;

          功能:內(nèi)存地址中的數(shù)據(jù)裝載到目標(biāo)寄存器Rd中,同時(shí)還可以把合成的有效地址寫回到基址寄存器。

          尋址方式:Rn:基址寄存器。Rm:變址寄存器。Index:偏移量,12位的無(wú)符號(hào)數(shù)。

          LDRRd,[Rn];把內(nèi)存中地址為Rn的字?jǐn)?shù)據(jù)裝入寄存器Rd中

          LDRRd,[Rn,Rm];將內(nèi)存中地址為Rn+Rm的字?jǐn)?shù)據(jù)裝入寄存器Rd中

          LDRRd,[Rn,#index];將內(nèi)存中地址為Rn+index的字?jǐn)?shù)據(jù)裝入Rd中

          LDRRd,[Rn,Rm,LSL#5];將內(nèi)存中地址為Rn+Rm×32的字?jǐn)?shù)據(jù)裝入Rd

          LDRRd,[Rn,Rm]!;將內(nèi)存中地址為Rn+Rm的字?jǐn)?shù)據(jù)裝入Rd,并將新地址Rn+Rm寫入Rn

          LDRRd,[Rn,#index]!;將內(nèi)存中地址為Rn+index的字?jǐn)?shù)據(jù)裝入Rd,并將新地址Rn+index寫入Rn

          LDRRd,[Rn,Rm,LSL#5]!;將內(nèi)存中地址為Rn+Rm×32的字?jǐn)?shù)據(jù)裝入Rd,并將新地址Rn+Rm×32寫入Rn

          LDRRd,[Rn],Rm;將內(nèi)存中地址為Rn的字?jǐn)?shù)據(jù)裝入寄存器Rd,并將新地址Rn+Rm寫入Rn

          LDRRd,[Rn],#index;將內(nèi)存中地址為Rn的字?jǐn)?shù)據(jù)裝入寄存器Rd,并將新地址Rn+index寫入Rn

          LDRRd,[Rn],Rm,LSL#5;將內(nèi)存中地址為Rn的字?jǐn)?shù)據(jù)裝入寄存器Rd,并將新地址Rn+Rm×32寫入Rn

          各指令的區(qū)別:

          (1)LDR字?jǐn)?shù)據(jù)加載指令

          將內(nèi)存地址中的字?jǐn)?shù)據(jù)裝載到目標(biāo)寄存器Rd中

          例:LDRR0,[R1,R2,LSL#5]!;將內(nèi)存中地址為R1+R2×32的字?jǐn)?shù)據(jù)裝入寄存器R0,并將新地址R1+R2×32寫入R1

          (2)LDRB字節(jié)數(shù)據(jù)加載指令

          同LDR,只是從內(nèi)存讀取一個(gè)8位的字節(jié)數(shù)據(jù)而不是一個(gè)32位的字?jǐn)?shù)據(jù),并將Rd的高24位清0。

          例:LDRBR0,[R1];將內(nèi)存中起始地址為R1的一個(gè)字節(jié)數(shù)據(jù)裝入R0中

          (3)LDRBT用戶模式的字節(jié)數(shù)據(jù)加載指令

          同LDRB指令,但無(wú)論處理器處于何種模式,都將該指令當(dāng)作一般用戶模式下的內(nèi)存操作。

          (4)LDRH半字?jǐn)?shù)據(jù)加載指令

          同LDR指令,但該指令只是從內(nèi)存讀取一個(gè)16位的半字?jǐn)?shù)據(jù)而不是一個(gè)32位的字?jǐn)?shù)據(jù),并將Rd的高16位清0。

          例:LDRHR0,[R1];將內(nèi)存中起始地址為R1的一個(gè)半字?jǐn)?shù)據(jù)裝入R0中

          (5)LDRSB有符號(hào)的字節(jié)數(shù)據(jù)加載指令

          同LDRB指令,但該指令將寄存器Rd的高24位設(shè)置成所裝載的字節(jié)數(shù)據(jù)符號(hào)位的值。

          例:LDRSBR0,[R1];將內(nèi)存中起始地址為R1的一個(gè)字節(jié)數(shù)據(jù)裝入R0中,R0的高24位設(shè)置成該字節(jié)數(shù)據(jù)的符號(hào)位

          (6)LDRSH有符號(hào)的半字?jǐn)?shù)據(jù)加載指令

          同LDRH指令,但該指令將寄存器Rd的高16位設(shè)置成所裝載的半字?jǐn)?shù)據(jù)符號(hào)位的值。

          例:LDRSHR0,[R1];將內(nèi)存中起始地址為R1的一個(gè)16位半字?jǐn)?shù)據(jù)裝入R0中,R0的高16位設(shè)置成該半字?jǐn)?shù)據(jù)的符號(hào)位

          (7)LDRT用戶模式的字?jǐn)?shù)據(jù)加載指令

          同LDR指令,但無(wú)論處理器處于何種模式,都將該指令當(dāng)作一般用戶模式下的內(nèi)存操作。有效地址必須是字對(duì)齊的

          2)單數(shù)據(jù)存儲(chǔ)指令

          格式:STR(或STR、STRB、STRBT、STRH、STRT),;

          功能:將寄存器數(shù)據(jù)寫入到內(nèi)存中

          尋址方式:Rn:基址寄存器。Rm:變址寄存器。Index:偏移量,12位的無(wú)符號(hào)數(shù)。

          STRRd,[Rn];將寄存器Rd中的字?jǐn)?shù)據(jù)寫入到內(nèi)存中地址為Rn內(nèi)存中

          STRRd,[Rn,Rm];將寄存器Rd中的字?jǐn)?shù)據(jù)寫入到內(nèi)存中地址為Rn+Rm的內(nèi)存中

          STRRd,[Rn,#index];將寄存器Rd中的字?jǐn)?shù)據(jù)寫入到內(nèi)存中地址為Rn+index內(nèi)存中

          STRRd,[Rn,Rm,LSL#5];將寄存器Rd中的字?jǐn)?shù)據(jù)寫入到內(nèi)存中地址為Rn+Rm×32內(nèi)存中

          STRRd,[Rn,Rm]!;將寄存器Rd中的字?jǐn)?shù)據(jù)寫入到內(nèi)存中地址為Rn+Rm的內(nèi)存中

          STRRd,[Rn,#index]!;將寄存器Rd中的字?jǐn)?shù)據(jù)寫入到內(nèi)存中地址為Rn+index的內(nèi)存中,并將新地址Rn+index寫入Rn

          STRRd,[Rn,Rm,LSL#5]!;將寄存器Rd中的字?jǐn)?shù)據(jù)寫入到內(nèi)存中地址為Rn+Rm×32的內(nèi)存中,并將新地址Rn+Rm×32寫入Rn

          STRRd,[Rn],Rm;將寄存器Rd中的字?jǐn)?shù)據(jù)寫入到內(nèi)存中地址為Rn的內(nèi)存中,并將新地址Rn+Rm寫入Rn

          STRRd,[Rn],#index;將寄存器Rd中的字?jǐn)?shù)據(jù)寫入到內(nèi)存中地址為Rn的內(nèi)存中,并將新地址Rn+index寫入Rn

          STRRd,[Rn],Rm,LSL#5;將寄存器Rd中的字?jǐn)?shù)據(jù)寫入到內(nèi)存中地址為Rn的內(nèi)存中,并將新地址Rn+Rm×32寫入Rn

          (1)STR字?jǐn)?shù)據(jù)存儲(chǔ)指令

          把寄存器Rd中的字?jǐn)?shù)據(jù)(32位)保存到addr所表示的內(nèi)存地址中,同時(shí)還可以把合成的有效地址寫回到基址寄存器。

          例:STRR0,[R1,#5]!;把R0中的字?jǐn)?shù)據(jù)保存到以R1+5為地址的內(nèi)存中,然后R1=R1+5

          (2)STRB字節(jié)數(shù)據(jù)存儲(chǔ)指令

          把寄存器Rd中的低8位字節(jié)數(shù)據(jù)保存到addr所表示的內(nèi)存地址中。

          例:STRBR0,[R1];將寄存器R0中的低8位數(shù)據(jù)存入R1表示的內(nèi)存地址中

          (3)STRBT用戶模式的字節(jié)數(shù)據(jù)存儲(chǔ)指令

          同STRB指令,但無(wú)論處理器處于何種模式,該指令都將被當(dāng)作一般用戶模式下的內(nèi)存操作。

          (4)STRH半字?jǐn)?shù)據(jù)存儲(chǔ)指令

          把寄存器Rd中的低16位半字?jǐn)?shù)據(jù)保存到addr所表示的內(nèi)存地址中,而且addr所表示的地址必須是半字對(duì)齊的。

          例:STRHR0,[R1];將寄存器R0中的低16位數(shù)據(jù)存入R1表示的內(nèi)存地址中

          (5)STRT用戶模式的字?jǐn)?shù)據(jù)存儲(chǔ)指令

          同STR指令,但無(wú)論處理器處于何種模式,該指令都將被當(dāng)作一般用戶模式下的內(nèi)存操作。

          2.多數(shù)據(jù)訪存指令

          1)批量數(shù)據(jù)加載指令

          格式:LDM{}{}{!},{^};

          功能:從一片連續(xù)的內(nèi)存單元讀取數(shù)據(jù)到各個(gè)寄存器中,內(nèi)存單元的起始地址為基址寄存器Rn的值,各個(gè)寄存器由寄存

          器列表regs表示。

          該指令一般用于多個(gè)寄存器數(shù)據(jù)的出棧。

          type字段種類:

          IA:每次傳送后地址加1。

          IB:每次傳送前地址加1。

          DA:每次傳送后地址減1。

          DB:每次傳送前地址減1。

          FD:滿遞減堆棧。

          ED:空遞減堆棧。

          FA:滿遞增堆棧。

          EA:空遞增堆棧。

          堆棧尋址的命令LDMFA/STMFA、LDMEA/STMEA、LDMFD/STMFD、LDMED/STMED。

          LDM和STM表示多寄存器尋址,即一次可以傳送多個(gè)寄存器值。

          LDM:一次裝載多個(gè),這里用來(lái)出棧。

          STM:一次存儲(chǔ)多個(gè),這里用來(lái)入棧。

          F/E表示指針指向的位置

          F:full滿堆棧,表示堆棧指針指向最后一個(gè)入棧的有效數(shù)據(jù)項(xiàng)。

          E:empty空堆棧,表示堆棧指針指向下一個(gè)要放入的空地址。

          A/D表示堆棧的生長(zhǎng)方式

          A:堆棧向高地址生長(zhǎng),即遞增堆棧。

          D:堆棧向低地址生長(zhǎng),即遞減堆棧。

          注意:有一個(gè)約定,編號(hào)低的寄存器在存儲(chǔ)數(shù)據(jù)或者加載數(shù)據(jù)時(shí)對(duì)應(yīng)于存儲(chǔ)器的低地址。

          FD、ED、FA和EA指定是滿棧還是空棧,是升序棧還是降序棧,用于堆棧尋址。

          一個(gè)滿棧的棧指針指向上次寫的最后一個(gè)數(shù)據(jù)單元.

          空棧的棧指針指向第一個(gè)空閑單元。

          一個(gè)降序棧是在內(nèi)存中反向增長(zhǎng)而升序棧在內(nèi)存中正向增長(zhǎng)。

          {!}:若選用了此后綴,則當(dāng)指令執(zhí)行完畢后,將最后的地址寫入基址寄存器。

          {^}:當(dāng)regs中不包含PC時(shí),該后綴用于指示指令所用的寄存器為用戶模式下的寄存器,

          否則指示指令執(zhí)行時(shí),將寄存器SPSR的值到CPSR中。

          2)批量數(shù)據(jù)存儲(chǔ)指令

          格式:STM{}{}{!},{^};

          功能:將各個(gè)寄存器的值存入一片連續(xù)的內(nèi)存單元中,內(nèi)存單元的起始地址為基址寄存器Rn的值

          各個(gè)寄存器由寄存器列表regs表示。該指令一般用于多個(gè)寄存器數(shù)據(jù)的入棧。

          {^}:指示指令所用的寄存器為用戶模式下的寄存器。其他參數(shù)用法同LDM指令。

          例:STMEAR13!,{R0-R12,PC};將寄存器R0~R12以及程序計(jì)數(shù)器PC的值保存到R13指示的堆棧中

          3.數(shù)據(jù)交換指令

          1)字?jǐn)?shù)據(jù)交換指令

          格式:SWP,,[];

          功能:Rd=[op2],[op2]=op1

          從op2所表示的內(nèi)存裝載一個(gè)字并把這個(gè)字放置到目的寄存器Rd中,然后把寄存器op1的內(nèi)容存儲(chǔ)到同一內(nèi)存地址中。

          例:SWPR0,R1,[R2];將R2所表示的內(nèi)存單元中的字?jǐn)?shù)據(jù)裝載到R0,然后將R1中的字?jǐn)?shù)據(jù)保存到R2所表示的內(nèi)存單元中

          2)字節(jié)數(shù)據(jù)交換指令

          格式:SWPB,,[];

          功能:從op2所表示的內(nèi)存裝載一個(gè)字節(jié)并把這個(gè)字節(jié)放置到目的寄存器Rd的低8位中,Rd的高24位設(shè)置為0;

          然后將寄存器op1的低8位數(shù)據(jù)存儲(chǔ)到同一內(nèi)存地址中。

          例:SWPBR0,R1,[R2];將R2所表示的內(nèi)存單元中的一個(gè)字節(jié)數(shù)據(jù)裝載到R0的低8位,然后將R1中的低8位字節(jié)

          數(shù)據(jù)保存到R2所表示的內(nèi)存單元中

          //^^^^^^^^^^^^^^^^^^^^^^^^^^下午^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

          (五)、協(xié)處理指令

          1)CDP協(xié)處理器操作指令

          格式:CDP{}

          ,,,,,;

          功能:用于傳遞指令給協(xié)處理器p,要求其在寄存器CRn和CRm上,進(jìn)行操作opcode1,并把結(jié)果存放到CRd中,

          可以使用opcode2提供與操作有關(guān)的補(bǔ)充信息。指令中的所有寄存器均為協(xié)處理器的寄存器,操作由協(xié)處理器完成。

          指令中:

          P為協(xié)處理器編號(hào);

          CRd為目的寄存器的協(xié)處理器寄存器;

          CRm和CRn為存放操作數(shù)的協(xié)處理器寄存器;

          Opcode1和opcode2為協(xié)處理器即將執(zhí)行的操作。

          例:CDPp5,5,c0,c1,c2,9;該指令用于通知協(xié)處理器p5,在c1和c2上執(zhí)行操作5和9,并將結(jié)果存放到c0中。

          2)LDC協(xié)處理器數(shù)據(jù)讀取指令

          格式:LDC{}{L}

          ,,;

          功能:將addr表示的內(nèi)存地址中的連續(xù)數(shù)據(jù)傳送到目的寄存器CRd中。

          L表示指令為長(zhǎng)讀取操作,比如用于雙精度數(shù)據(jù)的傳輸;

          目的寄存器CRd為協(xié)處理器的寄存器;

          addr的尋址方式同LDR指令,其寄存器為ARM處理器的寄存器。

          例:LDCp5,c1,[R1+5]:該指令用于將R1+5所對(duì)應(yīng)的存儲(chǔ)單元中的數(shù)據(jù),傳送到協(xié)處理器p5的寄存器c1中。

          3)STC協(xié)處理器數(shù)據(jù)存儲(chǔ)指令

          格式:STC{}{L}

          ,,;

          功能:將寄存器CRd的值傳送到addr表示的內(nèi)存地址中。指令中各參數(shù)用法同LDC。

          例如:STCp5,c1,[R1+5];該指令用于將協(xié)處理器p5中寄存器c1的數(shù)據(jù)傳送到R1+5所對(duì)應(yīng)的存儲(chǔ)單元中。

          4)MCRARM寄存器到協(xié)處理器寄存器的數(shù)據(jù)傳送指令

          格式:MCR{}

          ,,,,{,op2};

          功能:將ARM處理器的寄存器Rd中的數(shù)據(jù)傳送到協(xié)處理器p的寄存器CRn,CRm中;op1,op2為協(xié)處理器將要執(zhí)行的操作。

          例:MCRp5,5,R1,C1,C2,9;該指令將R1中的數(shù)據(jù)傳送到協(xié)處理器p5的寄存器C1,C2中,協(xié)處理器執(zhí)行操作5和9。

          MRC協(xié)處理器寄存器到ARM寄存器的數(shù)據(jù)傳送指令

          格式:MRC{}

          ,,,,{,op2};

          功能:將協(xié)處理器p的寄存器CRn,CRm的數(shù)據(jù)傳送到ARM處理器的寄存器Rd中;op1,op2為協(xié)處理器將要執(zhí)行的操作。

          例:MRCp5,5,R1,C1,C2,9;該指令將寄存器C1,C2中的數(shù)據(jù)傳送到R1中,協(xié)處理器p5協(xié)處理器執(zhí)行操作5和9。

          (六)、異常中斷指令

          異常中斷產(chǎn)生指令:用于系統(tǒng)調(diào)用和調(diào)試。

          1)SWI軟件中斷指令

          格式:SWI{}24位的立即數(shù);

          功能:用于產(chǎn)生軟件中斷,以使用戶程序調(diào)用操作系統(tǒng)的系統(tǒng)例程。

          指令中24位的立即數(shù)指定用戶程序調(diào)用系統(tǒng)例程的類型,其參數(shù)通過(guò)通用寄存器傳遞。當(dāng)24位的立即數(shù)

          被忽略時(shí),系統(tǒng)例程類型由寄存器R0指定,其參數(shù)通過(guò)其他通用寄存器傳遞。

          例:SWI0X05;調(diào)用編號(hào)為05的系統(tǒng)例程。

          2)BKPT斷點(diǎn)中斷指令

          格式:BKPT16位的立即數(shù);

          功能:用于產(chǎn)生軟件斷點(diǎn)中斷,以便軟件調(diào)試時(shí)使用。16位的立即數(shù)用于保存軟件調(diào)試中額外的斷點(diǎn)信息。

          指令操作的偽代碼:

          (七)、信號(hào)量操作指令

          信號(hào)量操作指令:用于進(jìn)程間的同步互斥,提供對(duì)信號(hào)量的原子操作。

          (八)、ARM程序常見(jiàn)結(jié)構(gòu)

          1.子函數(shù)和主函數(shù)

          使用BL指令進(jìn)行調(diào)用,該指令會(huì)把返回的PC值保存在LR

          AREAExample,CODE,READONLY;聲明代碼段Example

          ENTRY;程序入口

          Start

          MOVR0,#0;設(shè)置實(shí)參,將傳遞給子程騙子的實(shí)參存放在r0和r1內(nèi)

          MOVR1,#10

          BLADD_SUM;調(diào)用子程序ADD_SUM

          BOVER;跳轉(zhuǎn)到OVER標(biāo)號(hào)處,進(jìn)入結(jié)尾

          ADD_SUM

          ADDR0,R0,R1;實(shí)現(xiàn)兩數(shù)相加

          MOVPC,LR;子程序返回,R0內(nèi)為返回的結(jié)果

          OVER

          END

          運(yùn)行過(guò)程

          (1)程序從ENTRY后面指令處開(kāi)始運(yùn)行(即主函數(shù))

          R0和R1先準(zhǔn)備好數(shù)據(jù)令子函數(shù)運(yùn)算時(shí)使用

          (2)BL為跳轉(zhuǎn)指令,用于調(diào)用子函數(shù)后面為函數(shù)標(biāo)號(hào)。

          在調(diào)用函數(shù)的過(guò)程中,自動(dòng)將PC內(nèi)的值保存到LR(R14)內(nèi)備份,PC內(nèi)當(dāng)前的值為下一條要執(zhí)行的指令地址

          (即BOVER指令地址),在子函數(shù)結(jié)束前將該地址恢復(fù)到PC內(nèi)

          (3)B為跳轉(zhuǎn)指令,用于跳轉(zhuǎn)到指定的標(biāo)號(hào)后,此處跳轉(zhuǎn)到程序結(jié)尾

          (4)MOVPC,LR是子函數(shù)內(nèi)的最后一條語(yǔ)句,用于將LR內(nèi)保存的地址恢復(fù)到PC內(nèi)

          PC(R15)程序計(jì)數(shù)器存儲(chǔ)要執(zhí)行的指令在內(nèi)存中的地址

          PC的值=當(dāng)前正在執(zhí)行指令在內(nèi)存中的地址+8

          2.條件跳轉(zhuǎn)語(yǔ)句

          AREAExample,CODE,READONLY;聲明代碼段Example

          ENTRY;程序入口

          Start

          MOVR0,#2;將R0賦初值2

          MOVR1,#5;將R1賦初值5

          ADDR5,R0,R1;將R0和R1內(nèi)的值相加并存入R5

          CMPR5,#10

          BEQDOEQUAL;若R5為10,則跳轉(zhuǎn)到DOEQUAL標(biāo)簽處

          WAIT

          CMPR0,R1

          ADDHIR2,R0,#10;若R0>R1則R2=R0+10

          ADDLSR2,R1,#5;若R1<=R2則R2=R1+5

          DOEQUAL

          ANDSR1,R1,#0x80;R1=R1&0x80,并設(shè)置相應(yīng)標(biāo)志位

          BNEWAIT;若R1的d7位為1則跳轉(zhuǎn)到WAIT標(biāo)簽

          OVER

          END

          運(yùn)行過(guò)程

          (1)程序從ENTRY后面指令處開(kāi)始運(yùn)行(即主函數(shù))

          R0和R1賦初值2和5

          將R0與R1相加后存入R5內(nèi)

          (2)CMP用于比較兩個(gè)數(shù)據(jù),指令格式如下:

          CMP操作數(shù)1,操作數(shù)2

          CMP用于把一個(gè)寄存器的內(nèi)容和另一個(gè)寄存器或立即數(shù)進(jìn)行比較,同時(shí)

          更新CPSR中條件標(biāo)志位的值。標(biāo)志位表示操作數(shù)1和操作數(shù)2的關(guān)系。然后執(zhí)行后面的語(yǔ)句

          (3)條件助記符

          BEQB為跳轉(zhuǎn)指令,EQ為條件相等,讀取CPSR內(nèi)的條件標(biāo)志位,如相等則跳轉(zhuǎn)到所指定的標(biāo)號(hào)處

          BNEB為跳轉(zhuǎn)指令,NE為不相等(0),如不相等則跳轉(zhuǎn)到所指定的標(biāo)號(hào)處

          ADDHIADD為相加指令,HI為無(wú)符號(hào)大于,如大于則執(zhí)行相加

          ADDLSADD為相加指令,LS為無(wú)符號(hào)小于或等于,如小于或等于則相加

          (4)位運(yùn)算

          ANDSAND按位與,0x80取出第7位,S為將該位與的值寫入標(biāo)志位

          3.循環(huán)語(yǔ)句

          AREAExample,CODE,READONLY;聲明代碼段Example

          ENTRY;程序入口

          Start

          MOVR1,#0;將R1賦初值0

          LOOP

          ADDR1,R1,#1

          CMPR1,#10

          BCCLOOP;R1小于10則執(zhí)行跳轉(zhuǎn)到LOOP處執(zhí)行循環(huán),即R1從0到10后退出循環(huán)

          END

          例:編寫一具有完整匯編格式的程序,實(shí)現(xiàn)冒泡法排序功能。

          設(shè)無(wú)符號(hào)字?jǐn)?shù)據(jù)存放在從0x400004開(kāi)始的區(qū)域,字?jǐn)?shù)據(jù)的數(shù)目字存放在0x400000中。

          AREASORT,CODE,READONLY

          ENTRY

          START

          MOV R1,#0x400000

          LP

          SUBS R1,R1,#1

          BEQ EXIT

          MOV R7,R1

          LDR R0,=0x400004

          LP1

          LDR R2,[R0],#4

          LDR R3,[R0]

          CMP R2,R3

          STRLOR3,[R0,#-4]

          STRLOR2,[R0]

          SUBSR7,R7,#1

          BNE LP1

          B LP

          EXIT

          END

          練習(xí):

          1.編寫1+2+3+…+100的匯編程序。

          2.實(shí)現(xiàn)子函數(shù),該函數(shù)返回兩個(gè)參數(shù)中的最大值,在主函數(shù)內(nèi)調(diào)用。

          //*arm體系結(jié)構(gòu)第四天*

          四.偽操作和宏指令

          偽指令——是匯編語(yǔ)言程序里的特殊指令助記符,在匯編時(shí)被合適的機(jī)器指令替代。

          偽操作——為匯編程序所用,在源程序進(jìn)行匯編時(shí)由匯編程序處理,只在匯編過(guò)程起作用,不參與程序運(yùn)行。

          宏指令——通過(guò)偽操作定義的一段獨(dú)立的代碼。在調(diào)用它時(shí)將宏體插入到源程序中。也就是常說(shuō)的宏。

          說(shuō)明:所有的偽指令、偽操作和宏指令,均與具體的開(kāi)發(fā)工具中的編譯器有關(guān)

          1.宏定義(MACRO、MEND)

          格式:MACRO

          {$標(biāo)號(hào)名}宏名{$參數(shù)1,$參數(shù)2,……}

          指令序列

          MEND

          MACRO、MEND偽指令可以將一段代碼定義為一個(gè)整體,稱為宏指令,在程序中通過(guò)宏指令多次調(diào)用該段代碼。

          {}為可選項(xiàng)

          $標(biāo)號(hào)在宏指令被展開(kāi)時(shí),標(biāo)號(hào)會(huì)被替換為用戶定義的符號(hào)

          在宏定義體的第一行應(yīng)聲明宏的原型(包含宏名、所需的參數(shù)),然后就可以在匯編程序中通過(guò)宏名來(lái)調(diào)用該指令序列

          寫在代碼段或數(shù)據(jù)段前面

          MEXIT跳出宏

          例:沒(méi)有參數(shù)的宏(實(shí)現(xiàn)子函數(shù)返回)

          MACRO

          MOV_PC_LR;宏名

          MOVPC,LR;子程序返回,R0內(nèi)為返回的結(jié)果

          MEND

          AREAExample,CODE,READONLY;聲明代碼段Example

          ENTRY;程序入口

          Start

          MOVR0,#0;設(shè)置實(shí)參,將傳遞給子程騙子的實(shí)參存放在r0和r1內(nèi)

          MOVR1,#10

          BLADD_NUM;調(diào)用子程序ADD_NUM

          BLSUB_NUM;調(diào)用子程序SUB_NUM

          BOVER;跳轉(zhuǎn)到OVER標(biāo)號(hào)處,進(jìn)入結(jié)尾

          EXPORTADD_NUM

          ADD_NUM

          ADDR0,R0,R1;實(shí)現(xiàn)兩數(shù)相加

          MOV_PC_LR;調(diào)用宏,代表子函數(shù)結(jié)束

          EXPORTSUB_NUM

          SUB_NUM

          SUBR0,R1,R0;實(shí)現(xiàn)兩數(shù)相減

          MOV_PC_LR;調(diào)用宏,代表子函數(shù)結(jié)束

          OVER

          END

          例:有參數(shù)宏

          宏定義從MACRO偽指令開(kāi)始,到MEND結(jié)束,并可以使用參數(shù)。類似于C的#define

          MACRO;宏定義

          CALL$Function,$dat1,$dat2;宏名稱為CALL,帶3個(gè)參數(shù)

          IMPORT$Function;聲明外部子程序宏開(kāi)始

          MOVR0,$dat1;設(shè)置子程序參數(shù),R0=$dat1

          MOVR1,$dat2

          BL$Function;調(diào)用子程序宏最后一句

          MEND;宏定義結(jié)束

          CALLFADD1,#3,#2;宏調(diào)用,后面是三個(gè)參數(shù)

          匯編預(yù)處理后,宏調(diào)用將被展開(kāi),程序清單如下:

          IMPORTFADD1

          MOVR0,#3

          MOVR1,#3

          BLFADD1

          2.符號(hào)定義偽操作

          1)定義常量(EQU)

          格式:標(biāo)號(hào)名稱EQU表達(dá)式{,類型}

          用于為程序中的常量、標(biāo)號(hào)等定義一個(gè)等效的字符名稱,類似于C語(yǔ)言中的#define。

          其中EQU可用*代替。

          標(biāo)號(hào)名稱:為常量名。

          表達(dá)式:寄存器的地址值、程序中的標(biāo)號(hào)、32位地址常量、32位常量

          當(dāng)表達(dá)式為32位的常量時(shí),可以指定表達(dá)式的數(shù)據(jù)類型,可以有以下三種類型:

          CODE16、CODE32和DATA

          示例:EQU的使用

          XEQU45;即#defineX45,必須頂格

          YEQU64

          stack_topEQU0x30200000,CODE32

          AREAExample,CODE,READONLY

          CODE32

          ENTRY

          Start

          LDRSP,=stack_top;stack_top內(nèi)的值0x30200000是地址,=stack_top是取stack_top常量地址(即指針的指針)

          MOVR0,#X;將X替換為45

          STRR0,[SP];將R0內(nèi)的45存入到SP所指向的內(nèi)存中(SP此時(shí)是指針的指針)

          MOVR0,#Y

          LDRR1,[SP];從內(nèi)存中讀取數(shù)據(jù)到R1內(nèi)

          ADDR0,R0,R1

          STRR0,[SP]

          END

          注:X,Y,stack_top為標(biāo)號(hào),必須頂格寫,大多寫在代碼段外面

          2)定義變量

          常量:數(shù)字常量,有三種表示方式:十進(jìn)制數(shù)、十六進(jìn)制數(shù)、字符串常量、布爾常量(如testnoSETS{FALSE})

          變量:數(shù)字變量、邏輯變量、字符串變量

          (1)*GBLA、GBLL、GBLS定義全局變量

          格式:GBLA(GBLL、GBLS)全局變量名

          GBLA偽指令用于定義一個(gè)全局的數(shù)字變量,并初始化為0;

          GBLL偽指令用于定義一個(gè)全局的邏輯變量,并初始化為F(假);

          GBLS偽指令用于定義一個(gè)全局的字符串變量,并初始化為空;

          由于以上三條偽指令用于定義全局變量,因此在整個(gè)程序范圍內(nèi)變量名必須唯一。

          示例:全局變量的定義與賦值

          GBLAcount;定義全局變量

          countSETA2;給全局變量賦值為2,必須頂格

          AREAExample,CODE,READONLY

          CODE32

          ENTRY

          Start

          MOVR0,#count;將count內(nèi)的值寫入R0內(nèi)

          ADDR0,R0,#2

          BStart

          END

          注:在賦值過(guò)程中,全局變量名必須頂格寫,全局變量常在代碼段外定義和賦值

          示例:變量與內(nèi)存地址

          GBLAglobv

          globvSETA23

          AREAExample,CODE,READONLY;聲明代碼段Example

          ENTRY;程序入口

          Start

          LDRR0,=globv;golbv是全局變量,將內(nèi)存地址讀入到R0內(nèi)

          LDRR1,[R0];將內(nèi)存數(shù)據(jù)值讀入到R1內(nèi)

          ADDR1,R1,#2

          STRR1,[R0];將修改后數(shù)據(jù)再賦給變量

          MOVR0,#0

          OVER

          END

          注:#取變量值=取變量地址[R0]讀取R0內(nèi)地址所指向的數(shù)據(jù)值

          (2)*LCLA、LCLL、LCLS定義局部變量

          格式:LCLA(LCLL或LCLS)局部變量名

          LCLA偽指令用于定義一個(gè)局部的數(shù)字變量,并初始化為0;

          LCLL偽指令用于定義一個(gè)局部的邏輯變量,并初始化為F(假);

          LCLS偽指令用于定義一個(gè)局部的字符串變量,并初始化為空;

          以上三條偽指令必須寫在宏定義內(nèi),用于聲明局部變量,宏結(jié)束,局部變量不再起作用

          示例:

          LCLAnum;聲明一個(gè)局部的數(shù)字變量,變量名為num

          numSETA0xaa;將該變量賦值為0xaa

          LCLLisOk;聲明一個(gè)局部的邏輯變量,變量名為isOk

          isOkSETL;將該變量賦值為真

          LCLSstr1;定義一個(gè)局部的字符串變量,變量名為str1

          str1SETS"Testing";將該變量賦值為"Testing"

          示例:局部變量的定義與賦值

          MACRO

          MOV_START;宏名

          LCLAx;定義局部變量

          LCLAy

          xSETA12;必須頂格寫

          ySETA24

          MOVR0,#2

          MOVR1,#3

          ADDR0,R0,R1

          MEND

          AREAExample,CODE,READONLY;聲明代碼段Example

          ENTRY;程序入口

          Start

          MOV_START

          MOVR0,#0

          OVER

          END

          注:在賦值過(guò)程中,局部變量名必須頂格寫,局部變量必須在宏定義內(nèi)使用

          (3)*SETA、SETL和SETS用于給一個(gè)已經(jīng)定義的全局變量或局部變量賦值。

          SETA偽指令用于給一個(gè)數(shù)學(xué)變量賦值;

          SETL偽指令用于給一個(gè)邏輯變量賦值;

          SETS偽指令用于給一個(gè)字符串變量賦值;

          其中,變量名為已經(jīng)定義過(guò)的全局變量或局部變量,表達(dá)式為將要賦給變量的值。

          (4)變量代換$

          $在數(shù)字變量前,將變值轉(zhuǎn)換為十六進(jìn)制字符串

          $在邏輯變量前,將變量轉(zhuǎn)換為真或假

          $在字符串變量前,替換后面變量的字符串

          如:

          LCLSY1;定義局部字符串變量Y1和Y2

          LCLSY2

          Y1SETS"WORLD!"

          Y2SETS"LELLO,$Y1";將字符串Y2的值替換$Y1,形成新的字符串

          3)、定義一個(gè)寄存器(RN)

          格式:名稱RN表達(dá)式

          RN偽指令用于給一個(gè)寄存器定義一個(gè)別名。

          示例:

          TempRNR0;將R0定義一個(gè)別名Temp

          4)定義寄存器列表(RLIST)

          格式:名稱RLIST{寄存器列表}

          用于對(duì)一個(gè)通用寄存器列表定義名稱,使用該偽指令定義的名稱可在ARM指令LDM/STM中使用。

          在LDM/STM指令中,寄存器列表中的寄存器訪問(wèn)次序總是先訪問(wèn)編號(hào)較低的寄存器,再訪問(wèn)編號(hào)較高的寄存器,而不管寄存器列表中各寄存器的排列順序。

          示例:RegListRLIST{R0-R5,R8,R10};將寄存器列表名稱定義為RegList,用于多寄存器尋址(后面詳解)

          5)定義協(xié)處理器寄存器(CN)

          格式:名稱CN協(xié)處理器的寄存器編號(hào)

          示例:PowerCN6;將協(xié)處理器的寄存器6名稱定義為Power

          6)定義協(xié)處理器(CP)

          格式:名稱CP協(xié)處理器名

          示例:DmuCP6;將協(xié)處理器6名稱定義為Dmu

          7)定義浮點(diǎn)或精度寄存器(DN,SN,FN)

          格式:名稱DN雙精度寄存器編號(hào);DN為雙精度VFP寄存器定義名稱

          格式:名稱SN單精度寄存器編號(hào);SN為單精度VFP寄存器定義名稱

          格式:名稱FN浮點(diǎn)寄存器編號(hào);FN為浮點(diǎn)寄存器定義名稱

          示例:

          heightDN6;將VFP雙精度寄存器6名稱定義為height

          widthSN16;將VFP單精度寄存器16名稱定義為width

          heightFN6;將浮點(diǎn)寄存器6名稱定義為height

          //^^^^^^^^^^^^^^^^^^^^^^^^^^下午^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

          3.數(shù)據(jù)定義偽操作(申請(qǐng)內(nèi)存)

          數(shù)據(jù)定義偽指令用于為特定的數(shù)據(jù)分配存儲(chǔ)單元,同時(shí)可完成已分配存儲(chǔ)單元的初始化。

          1)按類型分配內(nèi)存

          格式:標(biāo)號(hào)偽指令表達(dá)式

          標(biāo)號(hào)

          表達(dá)式:初始化的值,表達(dá)式可以為程序標(biāo)號(hào)或字符、數(shù)字表達(dá)式

          偽指令:如下

          (1)DCB用于分配一片連續(xù)的字節(jié)存儲(chǔ)單元(字符數(shù)組),可用=號(hào)代替

          StrDCB"Thisisatest!";分配一片連續(xù)的字節(jié)存儲(chǔ)單元并初始化。

          (2)DCW(或DCWU)用于分配一片連續(xù)的半字存儲(chǔ)單元(16位短整型數(shù)組),DCW半字對(duì)齊,DCWU不嚴(yán)格半字對(duì)齊。

          DataTestDCW1,2,3

          (3)DCD(或DCDU)用于分配一片連續(xù)的字存儲(chǔ)單元(32位,整型數(shù)組),DCD可用&代替,DCD字對(duì)齊的

          DataTestDCD4,5,6

          (4)DCFD(或DCFDU)為雙精度的浮點(diǎn)數(shù)分配一片連續(xù)的字存儲(chǔ)單元,每個(gè)雙精度的浮點(diǎn)數(shù)占據(jù)兩個(gè)字單元。

          FDataTestDCFD2E115,-5E7

          (5)DCFS(或DCFSU)為單精度的浮點(diǎn)數(shù)分配一片連續(xù)的字存儲(chǔ)單元,每個(gè)單精度的浮點(diǎn)數(shù)占據(jù)一個(gè)字單元。

          FDataTestDCFS2E5,-5E-7

          (6)DCQ(或DCQU)用于分配一片以8個(gè)字節(jié)為單位的連續(xù)存儲(chǔ)區(qū)域(每8字節(jié)為一個(gè)數(shù)據(jù)的數(shù)組)

          DataTestDCQ100;分配一片連續(xù)的存儲(chǔ)單元并初始化為指定的值100。

          2)申請(qǐng)連續(xù)內(nèi)存

          (1)申請(qǐng)一個(gè)連續(xù)內(nèi)存(SPACE)

          用于分配一片連續(xù)的存儲(chǔ)區(qū)域并初始化為0,可用%代替

          格式:標(biāo)號(hào)SPACE表達(dá)式

          表達(dá)式為要分配的字節(jié)數(shù)

          例:DataSpaceSPACE100;分配連續(xù)100字節(jié)的存儲(chǔ)單元并初始化為0。

          (2)聲明一個(gè)數(shù)據(jù)緩沖池的開(kāi)始(LTORG)

          通常,把數(shù)據(jù)緩沖池放在代碼段的最后面,下一個(gè)代碼段之前,或END之前

          示例:

          AREAExample,CODE,READONLY;聲明代碼段Example

          ENTRY;程序入口

          Start

          BLfuncl

          funcl

          LDRR0,=0x12345678

          ADDR1,R1,R0

          MOVPC,LR

          LTORG;定義緩沖池0x12345678LTORG根據(jù)LDR確定內(nèi)存地址

          dataSPACE4200;從當(dāng)前位置開(kāi)始分配4200字節(jié)內(nèi)存

          END

          (3)定義一個(gè)結(jié)構(gòu)化的內(nèi)存表首地址(MAP)

          格式:MAP表達(dá)式{,基址寄存器}

          用于定義一個(gè)結(jié)構(gòu)化的內(nèi)存表的首地址。可用^代替。

          表達(dá)式可以為程序中的標(biāo)號(hào)或數(shù)學(xué)表達(dá)式,基址寄存器為可選項(xiàng).

          當(dāng)基址寄存器選項(xiàng)不存在時(shí),表達(dá)式的值即為內(nèi)存表的首地址,

          當(dāng)該選項(xiàng)存在時(shí),內(nèi)存表的首地址為表達(dá)式的值與基址寄存器的和。

          例:

          DatastrucSPACE280;分配280個(gè)字節(jié)單元

          MAPDatastruc;內(nèi)存表的首地址為Datastruc內(nèi)存塊

          (4)定義一個(gè)結(jié)構(gòu)化內(nèi)存表的數(shù)據(jù)域(FILED

          用于定義一個(gè)結(jié)構(gòu)化內(nèi)存表中的數(shù)據(jù)域??捎?代替

          格式:標(biāo)號(hào)FILED表達(dá)式

          FIELD偽指令常與MAP偽指令配合使用來(lái)定義結(jié)構(gòu)化的內(nèi)存表。表達(dá)式的值為當(dāng)前數(shù)據(jù)域所占的字節(jié)數(shù)。

          標(biāo)號(hào)為數(shù)據(jù)域(字段、成員變量)名

          MAP偽指令定義內(nèi)存表的首地址,

          FIELD偽指令定義內(nèi)存表中的各個(gè)數(shù)據(jù)域,并可以為每個(gè)數(shù)據(jù)域指定一個(gè)標(biāo)號(hào)供其他的指令引用。(3)內(nèi)存首地址(MAP)

          MAP偽指令通常與FIELD偽指令配合使用來(lái)定義結(jié)構(gòu)化的內(nèi)存表。

          示例:

          DatastrucSPACE280;分配280個(gè)字節(jié)單元

          MAPDatastruc;內(nèi)存表的首地址為Datastruc內(nèi)存塊

          constaFIELD4;字段consta長(zhǎng)度4字節(jié),相對(duì)地址0

          constabFIELD4;字段constab長(zhǎng)度4字節(jié),相對(duì)地址4

          xFIELD8;字段x長(zhǎng)度8字節(jié),相對(duì)地址8

          yFIELD8;字段y長(zhǎng)度8字節(jié),相對(duì)地址16

          stringFIELD256;字段string長(zhǎng)度256字節(jié),相對(duì)地址24

          LDRR6,[R9,consta];引用內(nèi)存表中的數(shù)據(jù)域

          注意:MAP偽操作和FIELD偽操作僅僅是定義數(shù)據(jù)結(jié)構(gòu),他們并不實(shí)際分配內(nèi)存單元,而SPACE用于分配內(nèi)存

          4.匯編控制偽操作

          用于控制匯編程序的執(zhí)行流程,常用的匯編控制偽指令包括以下幾條:

          (1)IF邏輯表達(dá)式...ELSE...ENDIF條件控制

          (2)WHILE邏輯表達(dá)式...WEND循環(huán)控制

          例:條件編譯

          AREAExample,CODE,READONLY

          CODE32

          Data_in*100;定義標(biāo)號(hào)Data_in的值為100在ENTRY入口之前

          GBLAcount;定義全局變量

          countSETA20

          ENTRY

          Start

          IFcount

          MOVR0,#3

          ELSE

          MOVR1,#24

          ENDIF

          MOVR1,#12

          ADDR0,R0,R1

          END

          例:循環(huán)編譯

          GBLACounter;聲明一個(gè)全局的數(shù)學(xué)變量,變量名為Counter

          CounterSETA3;由變量Counter控制循環(huán)次數(shù)

          ……

          WHILECounter<10

          指令序列

          IFcontinue

          MEXIT;退出宏

          ENDIF

          WEND

          5.其他常用的偽指令

          1)、AREA

          格式:AREA段名屬性1,屬性2,……

          AREA偽指令用于定義一個(gè)代碼段或數(shù)據(jù)段。其中,段名若以數(shù)字開(kāi)頭,則該段名需用"|"括起來(lái),如|1_test|。

          屬性字段表示該代碼段(或數(shù)據(jù)段)的相關(guān)屬性,多個(gè)屬性用逗號(hào)?

          —CODE屬性:用于定義代碼段,默認(rèn)為READONLY。

          —DATA屬性:用于定義數(shù)據(jù)段,默認(rèn)為READWRITE。

          —READONLY屬性:指定本段為只讀,代碼段默認(rèn)為READONLY。

          —READWRITE屬性:指定本段為可讀可寫,數(shù)據(jù)段的默認(rèn)屬性為READWRITE。

          —ALIGN屬性:使用方式為ALIGN表達(dá)式。在默認(rèn)時(shí),ELF(可執(zhí)行連接文件)的代碼段和數(shù)據(jù)段是按字對(duì)齊的

          —COMMON屬性:定義一個(gè)通用的段,不包含任何的用戶代碼和數(shù)據(jù)。各源文件中同名的COMMON段共享同一段存儲(chǔ)單元

          一個(gè)匯編語(yǔ)言程序至少要包含一個(gè)段,當(dāng)程序太長(zhǎng)時(shí),也可以將程序分為多個(gè)代碼段和數(shù)據(jù)段。

          使用示例:

          AREAInit,CODE,READONLY

          該偽指令定義了一個(gè)代碼段,段名為Init,屬性為只讀

          2)、ENTRY

          格式:ENTRY

          用于指定匯編程序的入口點(diǎn)。一個(gè)源文件里最多只能有一個(gè)ENTRY(可以沒(méi)有)。

          3)、END

          格式:END

          用于通知編譯器已經(jīng)到了源程序的結(jié)尾。

          4)、CODE16、CODE32

          格式:CODE16(或CODE32)

          CODE16偽指令通知編譯器,其后的指令序列為16位的Thumb指令。

          CODE32偽指令通知編譯器,其后的指令序列為32位的ARM指令。

          在使用ARM指令和Thumb指令混合編程的代碼里,可用這兩條偽指令進(jìn)行切換

          示例:

          AREAInit,CODE,READONLY

          CODE32;通知編譯器其后的指令為32位的ARM指令

          LDRR0,=NEXT+1;將跳轉(zhuǎn)地址放入寄存器R0

          BXR0;程序跳轉(zhuǎn)到新的位置執(zhí)行,并將處理器切換到Thumb工作狀態(tài)

          ……

          CODE16;通知編譯器其后的指令為16位的Thumb指令

          NEXTLDRR3,=0x3FF

          ……

          END;程序結(jié)束

          5)、EXPORT(或GLOBAL)

          格式:EXPORT標(biāo)號(hào)

          export偽指令用于在程序中聲明一個(gè)全局的標(biāo)號(hào),該標(biāo)號(hào)可在其他的文件中引用。

          6)、IMPORT

          格式:IMPORT標(biāo)號(hào)

          IMPORT偽指令用于通知編譯器要使用的標(biāo)號(hào)在其他的源文件中定義,但要在當(dāng)前源文件中引用

          如果當(dāng)前源文件實(shí)際并未引用該標(biāo)號(hào),該標(biāo)號(hào)也會(huì)被加入到當(dāng)前源文件的符號(hào)表中。

          7)、EXTERN

          格式:EXTERN標(biāo)號(hào)

          EXTERN偽指令用于通知編譯器要使用的標(biāo)號(hào)在其他的源文件中定義,但要在當(dāng)前源文件中引用

          如果當(dāng)前源文件實(shí)際并未引用該標(biāo)號(hào),該標(biāo)號(hào)就不會(huì)被加入到當(dāng)前源文件的符號(hào)表中。

          8)、GET(或INCLUDE)

          格式:GET文件名

          GET偽指令用于將一個(gè)源文件包含到當(dāng)前的源文件中,并將被包含的源文件在當(dāng)前位置進(jìn)行匯編處理。

          匯編程序中常用的方法是在某源文件中定義一些宏指令,用EQU定義常量的符號(hào)名稱,用MAP和FIELD定義結(jié)構(gòu)化的數(shù)據(jù)類型,然后用GET偽指令將這個(gè)源文件包含到其他的源文件中。使用方法與C語(yǔ)言中的"include"相似。

          GET偽指令只能用于包含源文件,包含目標(biāo)文件需要使用INCBIN偽指令

          示例:

          AREAInit,CODE,READONLY

          GETa1.s;通知編譯器當(dāng)前源文件包含源文件a1.s

          GETC:a2.s;通知編譯器當(dāng)前源文件包含源文件C:a2.s……

          END

          9)、INCBIN

          格式:INCBIN文件名

          INCBIN偽指令用于將一個(gè)目標(biāo)文件或數(shù)據(jù)文件包含到當(dāng)前的源文件中

          示例:

          AREAInit,CODE,READONLY

          INCBINa1.dat;通知編譯器當(dāng)前源文件包含文件a1.dat

          INCBINC:a2.txt;通知編譯器當(dāng)前源文件包含文件C:a2.txt……

          END

          10)、ROUT

          格式:{名稱}ROUT

          ROUT偽指令用于給一個(gè)局部變量定義作用范圍。

          在程序中未使用該偽指令時(shí),局部變量的作用范圍為所在的AREA,

          而使用ROUT后,局部變量的作為范圍為當(dāng)前ROUT和下一個(gè)ROUT之間。

          11)、ALIGN

          格式:ALIGN{表達(dá)式{,偏移量}}

          ALIGN偽指令可通過(guò)添加填充字節(jié)的方式,使當(dāng)前位置滿足一定的對(duì)其方式

          表達(dá)式的值用于指定對(duì)齊方式,可能的取值為2的冪,如1、2、4、8、16等。

          偏移量也為一個(gè)數(shù)字表達(dá)式,若使用該字段,則當(dāng)前位置的對(duì)齊方式為:2的表達(dá)式次冪+偏移量。

          示例:

          AREAInit,CODE,READONLY,ALIEN=3;指定后面的指令為8字節(jié)對(duì)齊。

          五.ARM匯編偽指令(讀取內(nèi)存地址)

          1)ADR及ADRL

          將PC相對(duì)偏移的地址或基于寄存器相對(duì)偏移的地址值讀取到寄存器中

          格式:ADR(ADRL)寄存器,地址表達(dá)式

          ADR小范圍的地址讀取偽指令,

          ADRL中等范圍的地址讀取偽指令

          例:

          查表

          ADRR0,D_TAB;加載轉(zhuǎn)換表地址

          LDRBR1,[R0,R2];使用R2作為參數(shù),進(jìn)行查表

          ……

          D_TAB

          DCB0xC0,0xF9,0xA4,0xB0,0x99,0x92

          2)LDR

          用于加載32位立即數(shù)或一個(gè)地址值到指定的寄存器,大范圍的地址讀取偽指令.

          LDR通常都是作加載指令,但是它也可以作偽指令。作用是裝載一個(gè)32bit常數(shù)和一個(gè)地址到寄存器。

          格式:LDR寄存器,=地址表達(dá)式

          COUNTEQU0x56000054;COUNT是一個(gè)變量,地址為0x56000054。

          LDRR1,=COUNT;將COUNT這個(gè)變量的值(地址),也就是0x56000054放到R1中。

          MOVR0,#0

          STRR0,[R1];是一個(gè)典型的存儲(chǔ)指令,將R0中的值放到以R1中的值為地址的存儲(chǔ)單元去

          ;這三條指令是為了完成對(duì)變量COUNT賦值。

          3)NOP

          空操作偽指令,可用于延時(shí)操作

          例:延時(shí)子程序

          Delay

          NOP;空操作

          NOP

          NOP

          SUBSR1,R1,#1;循環(huán)次數(shù)減1

          BNEDelay

          MOVPC,LR

          六、Thumb指令集

          有興趣的自學(xué)

          //*arm體系結(jié)構(gòu)第五天*

          七、C語(yǔ)言與匯編混合編程

          完全使用匯編語(yǔ)言來(lái)編寫程序會(huì)非常的繁瑣

          通常,只是使用匯編程序來(lái)完成少量必須由匯編程序才能完成的工作,而其它工作則由C語(yǔ)言程序來(lái)完成。

          (一)、ATPCS規(guī)則

          混合編程中,雙方都須遵守ATPCS規(guī)則。這些基本規(guī)則包括:

          子程序調(diào)用過(guò)程中寄存器的使用規(guī)則、數(shù)據(jù)棧的使用規(guī)則和參數(shù)的傳遞規(guī)則。

          1.寄存器使用規(guī)則

          寄存器:R4-R11用來(lái)保存局部變量

          R0-R3(a1-a4)用于保存參數(shù)/返回結(jié)果/scratch(臨時(shí)寄存器)

          R4-R11(v1-v8)用于保存ARM狀態(tài)局部變量

          R12(IP)子程序內(nèi)部調(diào)用的scratch

          R13(SP)數(shù)據(jù)棧指針

          R14(LR)連接寄存器

          R15(PC)程序計(jì)數(shù)器

          R7又可稱為wr用于Thumb狀態(tài)工作寄存器

          R9又可稱為sb在支持RWPI的ATPCS中為靜態(tài)基址寄存器

          R10又可稱為sl在支持PWPI的ATPCS中為數(shù)據(jù)棧限制指針

          R11又可稱為fp用于幀指針

          2.數(shù)據(jù)棧使用規(guī)則

          ATPCS標(biāo)準(zhǔn)規(guī)定,數(shù)據(jù)棧為FD(滿遞減類型),并且對(duì)數(shù)據(jù)棧的操作是8字節(jié)對(duì)齊。在進(jìn)行出棧和入棧操作,則

          必須使用ldmfd和strnfd指令(或ldmia和stmdb)

          3.參數(shù)的傳遞規(guī)則

          參數(shù):參數(shù)小于等于4,用R0-R3保存參數(shù),參數(shù)多于4,剩余的傳入堆棧

          函數(shù)返回:結(jié)果為32位整數(shù),通過(guò)R0返回

          結(jié)果為64位整數(shù),通過(guò)R0,R1返回

          對(duì)于位數(shù)更多的結(jié)果,通過(guò)內(nèi)存?zhèn)鬟f

          例:參數(shù)傳遞及結(jié)果返回(r0-r3做參數(shù),r0做返回值)

          AREAExample,CODE,READONLY;聲明代碼段Example

          ENTRY;程序入口

          Start

          MOVR3,#4;設(shè)置實(shí)參,將參數(shù)寫入R0-R3

          MOVR2,#3

          MOVR1,#2

          MOVR0,#1

          BLfunc1;調(diào)用子程序ADD_SUM

          BOVER;跳轉(zhuǎn)到OVER標(biāo)號(hào)處,進(jìn)入結(jié)尾

          func1

          ADDR0,R0,R1;實(shí)現(xiàn)兩數(shù)相加

          ADDR0,R0,R2

          ADDR0,R0,R3

          MOVPC,LR;子程序返回,R0內(nèi)為返回的結(jié)果

          OVER

          END

          相當(dāng)于如下C語(yǔ)言

          intfunc1(inta,intb,intc,intd){

          returna+b+c+d;

          }

          intmain(){

          func1(1,2,3,4);

          }

          例:多于4個(gè)參數(shù),前4個(gè)通過(guò)寄存器R0-R3傳遞,其它參數(shù)通過(guò)數(shù)據(jù)棧傳遞

          AREAExample,CODE,READONLY;聲明代碼段Example

          ENTRY;程序入口

          Start

          STMFDSP!,{R1-R4,LR};先將R1-R4,及LR內(nèi)原有數(shù)據(jù)壓入棧,需要使用這五個(gè)寄存器

          MOVR0,#1;準(zhǔn)備好7個(gè)寄存囂存入7個(gè)數(shù)據(jù)LR,IP,R4作臨時(shí)寄存器使用

          MOVIP,#2

          MOVLR,#3

          MOVR4,#4

          MOVR1,#5

          MOVR2,#6

          MOVR3,#7

          STMFDSP!,{R1-R3};先將R1-R3數(shù)據(jù)從前向后入棧,然后將IP,LR,R4內(nèi)的數(shù)據(jù)裝入R1-R3

          MOVR3,R4;其中IP,LR,R4是臨時(shí)使用的寄存器

          MOVR2,LR

          MOVR1,IP

          BLfunc1;調(diào)用子程序funclR0是返回結(jié)果

          LDMFDSP!,{R1-R4,PC};從棧中取出最初的數(shù)據(jù),恢復(fù)原始值

          BOVER;跳轉(zhuǎn)到OVER標(biāo)號(hào)處,進(jìn)入結(jié)尾

          func1

          STMFDSP!,{R4,LR};當(dāng)調(diào)用函數(shù)時(shí),LR和R4都已發(fā)生了變化,其中LR是指令地址所以也壓入棧

          LDRR4,[SP,#0x10];目前共壓入5個(gè)數(shù)據(jù),每一個(gè)數(shù)據(jù)占兩個(gè)字節(jié),當(dāng)前棧頂偏移10為前5個(gè)數(shù)據(jù)7

          ADDLR,SP,#8;將前第4個(gè)數(shù)據(jù)的地址(棧項(xiàng)+偏移)賦給LR

          LDMIALR,{IP,LR};連續(xù)將LR地址處的兩個(gè)數(shù)據(jù)取出寫入IP和LR內(nèi),從右向左寫,LDMIA即出棧指令

          ADDR0,R0,R1;從此行開(kāi)始相當(dāng)于returna+b+c+d+e+f+g;

          ADDR0,R0,R2

          ADDR0,R0,R3

          ADDR0,R0,IP

          ADDR0,R0,LR

          ADDR0,R0,R4

          LDMFDSP!,{R4,PC};從棧內(nèi)取數(shù)據(jù)加載入R4和PC,PC跳轉(zhuǎn)回主函數(shù)

          OVER

          END

          下面是棧頂

          相當(dāng)于如下C語(yǔ)言

          intfunc1(inta,intb,intc,intd,inte,intf,intg){

          returna+b+c+d+e+f+g;

          }

          intmain(){

          inta=1,b=2,c=3,d=4,e=5,f=6,g=7;

          func1(a,b,c,d,e,f,g);

          }

          (二)、C和ARM匯編程序間的相互調(diào)用

          1.匯編程序調(diào)用C子程序

          為保證程序調(diào)用時(shí)參數(shù)正確傳遞,必須遵守ATPCS。

          在C程序中函數(shù)不能定義為static函數(shù)。在匯編程序中需要在匯編語(yǔ)言中使用IMPORT偽操作來(lái)聲明C子函數(shù)

          //C代碼

          intsum5(inta,intb,intc,intd)

          {

          return(a+b+c+d);

          }

          //匯編代碼

          AREAExample,CODE,READONLY;聲明代碼段Example

          IMPORTsum5;

          ENTRY;程序入口

          Start

          MOVR3,#4;設(shè)置實(shí)參,將參數(shù)寫入R0-R3

          MOVR2,#3

          MOVR1,#2

          MOVR0,#1

          BLsum5;調(diào)用子程序sum5

          BOVER;跳轉(zhuǎn)到OVER標(biāo)號(hào)處,進(jìn)入結(jié)尾

          OVER

          END

          2.匯編程序訪問(wèn)全局C變量

          匯編程序中可以通過(guò)C全局變量的地址來(lái)間接訪問(wèn)C語(yǔ)言中定義的全局變量

          在編編程序中用IMPORT引入C全局變量,該C全局變量的名稱在匯編程序中被認(rèn)為是一個(gè)標(biāo)號(hào)。通過(guò)ldr和str指令訪問(wèn)該編號(hào)所代表的地址

          //C代碼

          inti=3;

          intsum5(inta,intb,intc,intd)

          {

          return(a+b+c+d+i);

          }

          //匯編代碼

          AREAExample,CODE,READONLY;聲明代碼段Example

          IMPORTsum5

          IMPORTi

          ENTRY;程序入口

          Start

          LDRR1,i;將i讀入R1內(nèi)

          MOVR0,#2

          ADDR0,R0,R1

          STRR0,i;將寄存器值寫入i內(nèi)

          MOVR3,#4;設(shè)置實(shí)參,將參數(shù)寫入R0-R3

          MOVR2,#3

          MOVR1,#2

          MOVR0,#1

          BLsum5;調(diào)用子程序ADD_SUM

          BOVER;跳轉(zhuǎn)到OVER標(biāo)號(hào)處,進(jìn)入結(jié)尾

          OVER

          END

          3.在C語(yǔ)言中調(diào)用匯編子程序

          為保證程序調(diào)用時(shí)參數(shù)的正確傳遞,在匯編程序中需要使用EXPORT偽操作來(lái)聲明匯編子程序,同時(shí)在C語(yǔ)言中使用extern擴(kuò)展聲明匯編子程序。

          //匯編代碼

          EXPORTfunc1;func1為子函數(shù)名

          AREAExample,CODE,READONLY;聲明代碼段Example

          func1

          ADDR0,R0,R1;實(shí)現(xiàn)兩數(shù)相加

          ADDR0,R0,R2

          ADDR0,R0,R3

          MOVPC,LR;子程序返回,R0內(nèi)為返回的結(jié)果

          END

          //C代碼

          externintfunc1(inta,intb,intc,intd);

          intmain(intargc,charargv){

          inta=1,b=2,c=3,d=4;

          intz=func1(a,b,c,d);

          printf("%d",z);

          return0;

          }

          4.在C語(yǔ)言中調(diào)用匯編全局變量

          匯編中用DCD為全局變量分配空間并賦值,并定義一個(gè)標(biāo)號(hào)代表該存儲(chǔ)位置。

          在匯編中用EXPORT導(dǎo)出標(biāo)號(hào)(這個(gè)標(biāo)號(hào)就是全局變量),在C程序中用extern擴(kuò)展聲明名該變量

          //匯編代碼

          EXPORTfunc1

          EXPORTtmp

          AREAExample,CODE,READONLY;聲明代碼段Example

          tmp;全局變量名

          DCD0x0005;全局變量創(chuàng)建內(nèi)存空間及賦初值

          func1;子函數(shù)名

          ADDR0,R0,R1;實(shí)現(xiàn)兩數(shù)相加

          ADDR0,R0,R2

          ADDR0,R0,R3

          MOVPC,LR;子程序返回,R0內(nèi)為返回的結(jié)果

          END

          //C代碼

          externintfunc1(inta,intb,intc,intd);

          externinttmp;

          intmain(intargc,charargv){

          inta=1,b=2,c=3,d=4;

          intz=func1(a,b,c,tmp);

          printf("%d",z);

          return0;

          }

          5.在C語(yǔ)言中內(nèi)嵌匯編

          有些操作C語(yǔ)言程序無(wú)法實(shí)現(xiàn),如改變CPSP寄存器值,初始化堆棧指針寄存器SP等,這些只能由匯編來(lái)完成。

          但出于編程簡(jiǎn)潔等一些因素,有時(shí)需要在C源代碼中實(shí)現(xiàn)上述操作,此時(shí)就需要在C中嵌入少量匯編代碼。

          內(nèi)嵌的匯編不能直接引用C的變量定義,必須通過(guò)ATPCS進(jìn)行,語(yǔ)法格式如下:

          __asm{

          //內(nèi)嵌匯編

          }

          例:在C語(yǔ)言中嵌入?yún)R編

          intf(){//C函數(shù)

          __asm{//內(nèi)嵌匯編,禁用中斷例子

          MRSR0,CPSR

          ORRR0,R0,#0x80

          MSRCPSR_c,R0

          }

          }

          intmain(intargc,charargv){

          inta;

          intz=f(a);

          printf("%d",z);

          return0;

          }

          出地完整性考慮,內(nèi)嵌匯編相對(duì)于一般匯編的不同特點(diǎn)如下:

          1)操作數(shù)可以是寄存器、常量或C表達(dá)式。可以是char、short、或int類型,而且是無(wú)符號(hào)數(shù)進(jìn)行操作

          2)常量前的#號(hào)可以省略

          3)只有指令B可以使用C程序中的標(biāo)號(hào),指令BL不可以使用

          4)不支持匯編語(yǔ)言中用于內(nèi)存分配的偽操作

          5)內(nèi)嵌匯編不支持通過(guò)“.”指示符或PC獲取當(dāng)前指令地址

          6)不支持LDRRn,=expression偽指令,而使用MOVRn,expression指令向寄存器賦值

          7)不支持標(biāo)號(hào)表達(dá)式

          8)不支持ADR和ADRL偽指令

          9)不支持BX和BLX指令

          10)不可以向PC賦值

          11)使用0x前綴替代&表示十六進(jìn)制數(shù)

          12)不使用寄存尋址變量

          13)ldm和stm指令的寄存器列表只允許物理寄存器

          14)必須小心使用物理寄存器,如R0-R3,LR和PC

          //------------------------------------------

          (三)、裸機(jī)硬件的控制方法

          1.裸板開(kāi)發(fā)環(huán)境搭建

          1)J-Link

          (1)安裝Setup_JLinkARM_V408i.exe

          (2)連接開(kāi)發(fā)板

          1打開(kāi)桌面快捷J-FlashARMV4.08i

          2連接好開(kāi)發(fā)板開(kāi)發(fā)板->jlink->pc(usb)

          3將開(kāi)發(fā)板置為NorFlash啟動(dòng)

          4打開(kāi)菜單file->openproject->選擇TQ2440.jflash

          5填加配置選項(xiàng)

          將Flash.csv到安裝目錄的ETCJFlash內(nèi)

          打開(kāi)菜單options->projectsettings

          在彈出對(duì)話框內(nèi)選flash后,點(diǎn)擊按鈕selectflashdevice

          在彈出對(duì)話框內(nèi)選擇EN29LV160AB

          6.連接開(kāi)發(fā)板

          重啟開(kāi)發(fā)板,然后點(diǎn)擊菜單target->connect查看聯(lián)接信息

          (3)燒寫方法

          將j-link連接好后,在菜單file->open內(nèi)選擇要燒寫的程序。

          如:燒寫u-boot.bin,

          然后在燒寫地址對(duì)話框內(nèi)輸入燒寫地址,u-boot的地址設(shè)為0

          再點(diǎn)擊菜單target->program進(jìn)行燒寫

          (4)調(diào)試方法

          連接JLINK和開(kāi)發(fā)板。

          打開(kāi)程序J-LINKARMV4.08i->J-linkGDBServer

          設(shè)置信息JTAGspeed為500KH

          所有選項(xiàng)勾選

          設(shè)置AXD調(diào)試環(huán)境options->configuretarget填加JlinkARM目錄下的JLinkRDI.dll

          然后在AXD內(nèi)選則JLinkRDI.dll選項(xiàng),同時(shí)點(diǎn)擊右側(cè)configure按鈕

          在彈出對(duì)話框內(nèi)General標(biāo)簽:JTAGspeed設(shè)為4000Khz

          flash標(biāo)簽:去掉Enableflashprogramming選項(xiàng)

          Breakpoints:去掉Usesoftwarebreakpoints選項(xiàng)

          CPU標(biāo)簽:勾選Allowinstructionsetsimulation

          Littleendian

          Resetstrategy內(nèi)選

          Hardware,haltafterreset(normal)和1000ms

          在AXD內(nèi),通過(guò)File->LoadImage載入要調(diào)試的axf文件

          (四)、軟件控制硬件的編程原理

          每一種硬件,在其控制器芯片上都有物理寄存器(不是CPU寄存器,是硬件上的寄存器)

          這些寄存器分為三類:命令寄存器、狀態(tài)寄存器、數(shù)據(jù)寄存器

          程序控制硬件的方法是,通過(guò)匯編str指令向命令寄存器寫入指令即可完成對(duì)硬件的配置操作或執(zhí)行物理操作。

          通過(guò)匯編ldr指令從數(shù)據(jù)寄存器中獲取數(shù)據(jù),或從狀態(tài)寄存器獲取狀態(tài)。

          程序控制硬件,其實(shí)質(zhì)就是對(duì)硬件的寄存器進(jìn)行讀寫操作。

          程序中需要解決的問(wèn)題:

          1)硬件寄存器的內(nèi)存地址是多少?

          2)向哪一個(gè)寄存器寫入什么值?

          如想解決上述兩個(gè)問(wèn)題,需要熟練查看硬件的手冊(cè),閱讀硬件連線原理圖。

          數(shù)電的基本知識(shí):

          1).電路符號(hào)

          2).基本原理

          以LED燈為例講解如何使用硬件手冊(cè),和原理圖

          在開(kāi)發(fā)板上有4個(gè)LED燈,如果想點(diǎn)亮,必須先看硬件連線圖

          1.查看開(kāi)發(fā)板LED管腳連線

          TQ2400開(kāi)發(fā)板有兩塊電路板:核心板、主板

          LED燈在主板上,所以查看《TQ2440底板原理圖.pdf》,找到LED模塊(LED測(cè)試)

          從電路圖可看到如果讓LED1燈點(diǎn)亮,必須在nLED_1連線上輸出低電屏(即通電)

          如何讓LED1上輸出低電屏,需查看CPU相關(guān)引腳連線

          2.查看TQ2440核心板原理圖

          CPU在核心板上,所以查看《TQ2440_V2核心板原理圖.pdf》

          找到nLED1連線,如下

          nLED1在CUP的GPB5引腳上

          nLED2GPB6

          nLED3GPB7

          nLED4GPB8

          如何讓CPU的GPB5為低電屏,需查看CPU引腳模式

          3.查看CPU用戶手冊(cè)

          TQ2440的微處理器芯片為S3C2440,所以查看《S3C2440.pdf》芯片手冊(cè),找到GPB5的管腳模式

          1)控制寄存器

          GPB5[11:10]00=Input01=Output

          10=nXBACK11=Reserved

          GPBCON控制寄存器

          GPBDAT數(shù)據(jù)寄存器

          GPBUP上拉使能寄存器

          [11:10]是所在位,每個(gè)管腳都是占兩個(gè)位

          其中Input是輸入值

          Output輸出值

          reserved保留值

          nXBACKnXBACK/GPB5

          即,如果將GPB5設(shè)為低電屏,則需要將控制寄存器設(shè)置為輸出模式。

          即需要CPU寄存器GPBCON的11和10位設(shè)置為01即11位為010位為1

          2)數(shù)據(jù)寄存器

          GPB[10:0][10:0]當(dāng)端口配置為輸入端口時(shí),相應(yīng)位為引腳狀態(tài)。

          當(dāng)端口配置為輸出端口時(shí),相應(yīng)位為引腳狀態(tài)。

          當(dāng)端口配置為功能引腳時(shí),將讀取到未定義值

          所以,當(dāng)GPBCON為輸入狀態(tài)時(shí),GPBDAT的相應(yīng)位5則應(yīng)為0才會(huì)輸出低電屏

          3)查看寄存器地址

          GPBCON0x56000010R/W端口配置寄存器復(fù)位值0x0

          GPBDAT0x56000014R/W端口數(shù)據(jù)寄存器復(fù)位值-

          所以,需要將地址位0x56000010的寄存器作為GPBCON寄存器

          地址位0x56000014的寄存器作為GPBDAT寄存器

          4.代碼編寫

          //ledtest.c

          #defineGPBCON(*(volatileunsignedlong*)0x56000010)

          #defineGPBDAT(*(volatileunsignedlong*)0x56000014)

          //volatile影響編譯器編譯的結(jié)果,表示變量是隨時(shí)可能發(fā)生變化的

          //0x56000010是地址,強(qiáng)行轉(zhuǎn)換為unsignedlong*類型,

          //然后前面加*代表這個(gè)地址所指向的寄存器變量

          #defineLEDS(1<<5|1<<6|1<<7|1<<8)//即11110000

          #defineDELAYVAL(0xFFFF)

          externintdelay(inttime);//聲明匯編函數(shù)

          intmain(){

          intval;

          inti=0;

          GPBCON=(1<<16|1<<14|1<<12|1<<10);//即010101010000000000即B5B6B7B8的狀態(tài)寄存器

          val=val+1;

          for(i=0;i<4;i++){

          GPBDAT=(GPBDAT&(~LEDS))|(1<<6|1<<7|1<<8);

          delay(DELAYVAL);

          GPBDAT=(GPBDAT&(~LEDS))|(1<<5|1<<7|1<<8);

          delay(DELAYVAL);

          GPBDAT=(GPBDAT&(~LEDS))|(1<<5|1<<6|1<<8);

          delay(DELAYVAL);

          GPBDAT=(GPBDAT&(~LEDS))|(1<<5|1<<6|1<<7);

          delay(DELAYVAL);

          }

          return0;

          }

          //delay.s

          ;匯編指令延時(shí)程序

          EXPORTdelay

          AREADELAY,CODE,READONLY

          ;下面是延時(shí)子程序

          delay

          subR0,R0,#1;r0=r0-1

          cmpR0,#0x0;將r0與0比較

          bnedelay;比較的結(jié)果不為0,則繼續(xù)調(diào)用delay

          movPC,LR;返回

          END

          5.調(diào)試代碼

          2440ART.mcp內(nèi)雙擊

          TargetSettins:post-linker選擇ArMfromELF

          LanguageSettins:ArchitectureorProcessor選擇相應(yīng)的編譯器ARM920T

          ArmLinker:output內(nèi)RO0x30000000

          options內(nèi)Imageentrypoint設(shè)為0x30000000

          layout內(nèi)Object2440init.oSectionInit

          Listings內(nèi)Imagemap

          ArmfromELF:outputformat內(nèi)Plainbinary

          outputfilename內(nèi)*.bin

          編譯

          make

          調(diào)試AXD是調(diào)試器

          設(shè)置,debug->打開(kāi)AXD調(diào)試界面,選擇option->configtarget選項(xiàng)

          選ARMUL(模擬調(diào)試器),然后選擇確定.進(jìn)入調(diào)試界面.

          ARMUL是虛擬調(diào)試環(huán)境(虛擬開(kāi)發(fā)板)

          如果用開(kāi)發(fā)板真實(shí)環(huán)境調(diào)試,則需要使用JTAG連開(kāi)發(fā)板后,在此處選H-JTAG

          用file-

          execute->runtocousor項(xiàng).使程序進(jìn)入用戶主程序

          可以用F8來(lái)一條一條執(zhí)行語(yǔ)句,也可用F10,可以設(shè)置斷點(diǎn).



          關(guān)鍵詞: arm匯編編

          評(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); })();