arm匯編編程(示例)
(一).arm的基本概念
本文引用地址:http://dyxdggzs.com/article/201611/317880.htm1.什么是arm
arm是一家英國電子公司的名字,全名是AdvancedRISCMachine
這家企業(yè)設計了大量高性能、廉價(jià)、耗能低的RISC(精簡(jiǎn)指令集)處理器,ARM公司只設計芯片而不生產(chǎn),它將
技術(shù)授權給世界上許多公司和廠(chǎng)商。目前采用arm技術(shù)知識產(chǎn)權內核的微處理器,即通常所說(shuō)的arm微處理器
所以arm也是對一類(lèi)微處理器的通稱(chēng)。
arm指令集體系版本號(軟件)為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è)控制:如機床、自動(dòng)控制等
無(wú)線(xiàn)通信:如手機
網(wǎng)絡(luò )應用:如
電子產(chǎn)品:如音視頻播放噐、機頂盒、游戲機、數碼相機、打印機
其它各領(lǐng)域:如軍事、醫療、機器人、智能家居等
4.計算機體系結構
見(jiàn)圖:馮.諾依曼計算機體系圖
馮.諾依曼體系結構
處理器使用同一個(gè)存儲器,經(jīng)由同一個(gè)總線(xiàn)傳輸
完成一條指令需要3個(gè)步驟:即取指令->指令譯碼->執行指令
指令和數據共享同一總線(xiàn)的結構
哈佛體系結構
將程序指令存儲和數據存儲分開(kāi)
中央處理器首先到程序指令存儲器中讀取程序指令。解碼后到數據地址,再到相應的數據存儲器讀取數據,然后執行指令
程序指令存儲與數據存儲分開(kāi),可以使指令和數據有不同的數據寬度。
5.復雜指令集與精簡(jiǎn)指令集
CISC復雜指令集:采用馮.諾依曼體系結構。數據線(xiàn)和指令線(xiàn)分時(shí)復用(只能通過(guò)一輛車(chē))。
存儲器操作指令多匯編程序相對簡(jiǎn)單指令結束后響應中斷CPU電路豐富面積大功耗大
RISC精簡(jiǎn)指令集:采用哈佛體系結構。數據線(xiàn)和指令線(xiàn)分離(同時(shí)能通過(guò)多輛車(chē))。
對存儲器操作有限匯編程序占空間大在適當地方響應中斷CPU電路較少體積小功耗低
ARM采用RISC精簡(jiǎn)指令集
Thumb是ARM體系結構中一種16位的指令集。
從ARMv4T之后,的ARM處理器有一種16-bit指令模式,叫做Thumb,較短的指令碼提供整體更佳的編碼密度,更有效地使用有限的內存帶寬。所有ARM9和后來(lái)的家族,包括XScale都納入了Thumb技術(shù)。
即ARM有兩種指令集:RISC、Thumb
6.arm的思想
1)arm體系的總思想:
在不犧牲性能的同時(shí),盡量簡(jiǎn)化處理器。同時(shí)從體系結構上靈活支持處理器擴展。采用RISC結構。RISC處理器簡(jiǎn)化了處理器結構,減少復雜功能指令的同時(shí),提高了處理器速度。
ARM及MIPS都是典型的RISC處理器
2)arm的流水線(xiàn)結構
arm處理器使用流水線(xiàn)來(lái)增加處理器指令流的速度,這樣可以使幾個(gè)操作同時(shí)進(jìn)行。并使處理和存儲器系統連續操作。
arm處理器分為三級:取指->譯碼->執行
取指:指令從存儲器中取出
譯碼:對指令使用的寄存器進(jìn)行譯碼
執行:從寄存器組中讀取寄存器,執行移位和ALU操作,寄存器被寫(xiě)回到寄存器組中
3)ARM處理器支持的類(lèi)型
字節8位
半字16位
字32位
所有數據操作都以字為單位
ARM指令的長(cháng)度剛好是一個(gè)字,Thumb指令長(cháng)度剛好是半個(gè)字
4)ARM處理器狀態(tài)
ARM處理器內核使用ARM結構,該結構包含32位的ARM指令集和16位Thumb指令集,因此ARM有兩種操作狀態(tài)
ARM狀態(tài):32位
Thumb狀態(tài):16位
5)處理器模式
ARM處理器共有7種運行模式:
用戶(hù):正常程序工作模式,不能直接切換到其它模式
系統:用于支持操作系統的特權任務(wù),可以直接切換到其它模式
快中斷:支持高速數據傳輸及通道處理,FIQ異常響應時(shí)進(jìn)入此模式
中斷:用于通用中斷處理,IRQ異常響應時(shí)進(jìn)入此模式
管理:操作系統保護代碼,系統復位和軟件中斷響應時(shí)進(jìn)入此模式
中止:用于支持虛擬內存或存儲器保護,用于MMU
未定義:支持硬件協(xié)處理器的軟件仿真,未定義指令異常響應時(shí)進(jìn)入此模式。
(二)、經(jīng)典平臺硬件組成
見(jiàn)圖:arm硬件組成圖
開(kāi)發(fā)板一般是由一塊組成的,有核心器件和外圍器件接口等,但是有的是由兩塊板子組成,主版和核心板,主版上主要是外圍接口,外圍器件等,核心板上主要是核心器件,還有一些晶振電路等
1.核心板(天嵌2440)
CPU處理器S3C2440AL,主頻400MHz(最高可達533MHz)
SDRAM內存板載64MBSDRAM(標準配置),32bit數據總線(xiàn)SDRAM時(shí)鐘頻率高達100MHz(支持運行133MHz)
NandFlash板載64MBNandFlash或256MBNandFlash(標準配置)
NorFlash板載2MBNorFlash(最高可升級到8MB)
CorePower專(zhuān)業(yè)1.25V核心電壓供電
Power核心板采用3.3V供電
Powerled核心板電源指示燈
核心板接口接口型號為DC-2.0雙列直插
SDRAM:隨機存儲器,普遍使用的內存。用作主存。
NORFlash和NANDFlash是現在市場(chǎng)上兩種主要的非易失閃存。
NOR的特點(diǎn)是芯片內執行,應用程序可以直接在flash閃存內運行,不必再把代碼讀到系統RAM中。
NAND結構能提供極高的單元密度,可以達到高存儲密度,并且寫(xiě)入和擦除的速度也很快。
2.主板
電源
并口線(xiàn)
復位
RTC電源
RS232電平轉換DB9插座
音頻IIS,AC97
按鍵、PS/2與IC接口
數碼管
觸摸屏
以太網(wǎng)卡
主USBHUB1轉4
3.寄存器
見(jiàn)圖:ARM模塊和內核框圖
寄存器是中央處理器內的組成部份。
寄存器是有限存貯容量的高速存貯部件,用來(lái)暫存指令、數據和位址。在中央處理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序計數器(PC)。在中央處理器的算術(shù)及邏輯部件中,包含的寄存器有累加器(ACC)。
IR用于存儲指令
PC用于存儲程序運行的地址(即當前指令在內存中的位置)
寄存器是由一個(gè)指令的輸出或輸入可以直接索引到的暫存器群組。所有的計算機指令都是進(jìn)入寄存器后被直接讀取
ARM的匯編編程,本質(zhì)上就是針對CPU寄存器的編程。
//*重點(diǎn)需要背訟*
ARM寄存器分為2類(lèi):普通寄存器和狀態(tài)寄存器
(1)通用寄存器和計數器:共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)數據寄存器
R15(PC)程序計數器它的值是當前正在執行的指令在內存中的位置。
當指令執行結束后,CPU會(huì )自動(dòng)將PC值加上一個(gè)單位,PC值指向下一條即將執行的指令的地址
如果通過(guò)匯編指令對PC寄存器賦值,就會(huì )完成一次程序的跳轉(如從子函數跳轉回主函數內)
R14(LR)鏈接寄存器存放子程序的返回地址
例如:在主函數內,如果調用子函數,程序會(huì )進(jìn)入到子函數內執行。當子函數執行完畢后,需要回到
主函數內,所以,在子函數調用前需要將這個(gè)地址先保存起來(lái),否則無(wú)法找到這個(gè)地址。
LR用于保存這個(gè)地址,這個(gè)地址也稱(chēng)為子程序返回地址。當子函數結束后,再將LR內的地址賦給PC即可。
如果子程序再調用孫程序,LR如何保存地址呢?
先把當前LR內的值壓入內存的棧區,然后LR再保存孫程序的返回地址。當孫程序執行完后通過(guò)PC跳轉到
子程序內,此時(shí)將棧區內的子程序返回地址取出保存在LR內。當子程序執行完后,再通過(guò)PC跳轉到主函數內。
R13(SP)棧指針寄存器用于存放堆棧的棧頂地址。
SP相當于指針變量,保存的是棧頂的地址,出棧時(shí),從SP指向的內存中取出數據,入棧時(shí)將新的內存地址
壓入棧頂,而SP相當于鏈表的頭指針(head)。
原則上說(shuō)R0-R12可以保存任何數據。其中R0-R7用來(lái)臨時(shí)存儲數據,R8-R12系統沒(méi)有用來(lái)做任何特殊用途,常用于中斷
而在匯編與C語(yǔ)言的交互中,定制了ATPCS標準
寄存器:R4-R11用來(lái)保存局部變量
參數:參數小于等于4,用R0-R3保存參數,參數多于4,剩余的傳入堆棧
函數返回:結果為32位整數,通過(guò)R0返回
結果為64位整數,通過(guò)R0,R1返回
對于位數更多的結果,通過(guò)內存傳遞
(2)狀態(tài)寄存器:
狀態(tài)寄存器用于保存程序的當前狀態(tài)
CPSR當前程序狀態(tài)寄存器
一個(gè)寄存器為32位,每一位數據代表不同的狀態(tài)。分為三個(gè)部分(條件代碼標志位、控制位、保留區位)
31322928....76543210
NZCVIFTM4M3M2M1M0
其中NZCV稱(chēng)為條件標志位(即保存的是條件的運算結果,真和假)
N=1表示運算結果為負數,N=0表示運算結果為正數。
Z=1表示運算結果為0,Z=0表示運算結果為非零。
C=1表示運算結果產(chǎn)生了進(jìn)位。
V=1運算結果的符號位發(fā)生了溢出。
這4個(gè)位的組合,代表了各種條件,如下:
0000 EQ Z置位相等/等于0
0001 NE Z清0不等
0010 CS/HS C置位進(jìn)位/無(wú)符號高于或等于
0011 CC/LO C清0無(wú)進(jìn)位/無(wú)符號低于
0100 MI N置位負數
0101 PL N清0非負數
0110 VS V置位溢出
0111 VC V清0無(wú)溢出
1000 HI C置位且Z清0無(wú)符號高于
1001 LS C清0或Z置位無(wú)符號低于或等于
1010 GE N等于V有符號大于或等于
1011 LT N不等于V有符號小于
1100 GT Z清0且N等于V有符號大于
1101 LE Z置位或N不等于V有符號小于或等于
1110 AL 任何狀態(tài) 總是(always)
1111 NV 無(wú) 從不(never)
IFT稱(chēng)為控制位
II=1禁用IRO中斷
FF=1禁用FIQ中斷
T表示CPU當前的狀態(tài),1代表正在Thumb指令集狀態(tài),0表示正在A(yíng)RM指令集狀態(tài)。
M0至M4表示中斷類(lèi)型(控制位內的模式位)
0b10000User用戶(hù)中斷
0b10001FIQ快速中斷
0b10010IRQ聲卡、調制解調器等外部設備產(chǎn)生的中斷
0b10011Supervisor管理程序或監控程序產(chǎn)生的中斷
0b10111Abort異常中斷
0b11011Undefined未定義中斷
0b11111System系統中斷
SPSR保存的程序狀態(tài)寄存器,結構與CPSR完全一樣,用來(lái)保存CPSR的值。以便出現異常時(shí)恢復CPSR的值
(3)流水線(xiàn)對pc值的影響
CPU內部的組成部分:指令寄存器,指令譯碼器,指令執行單元(包括ALU和通用寄存器組)
CPU執行指令的步驟:取指->譯碼->執行
取指:將指令從內存或指令cache中取入指令寄存器
譯碼:指令譯碼器對指令寄存器中的指令進(jìn)行譯碼操作,辨識add,或是sub等操作
執行:指令執行單元根據譯碼的結果進(jìn)行運算并保存結果
流水線(xiàn)操作:并發(fā)多條流水線(xiàn)(以3條為例)
1取指譯碼執行
2取指譯碼執行
3取指譯碼執行
提高時(shí)間效率
(三)、學(xué)習內容
1.匯編(對裸板機的控制,以及驅動(dòng)程序控制)
2.內核移植(uboot移植、內核編譯、文件系統移植、應用程序移植)
3.驅動(dòng)程序編寫(xiě)
//^^^^^^^^^^^^^^^^^^^^^^^^^^下午^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(四)、ADS的使用
ADS是匯編或C語(yǔ)言編譯調試工具
可生成的文件:.axf含調試信息的可執行ELF文件
.bin可燒寫(xiě)的二進(jìn)制映像文件
.hex可燒寫(xiě)的十六進(jìn)制映像文件
ADS配置
在磁盤(pán)中新建一個(gè)目錄D:arm,用來(lái)存儲所寫(xiě)的代碼
點(diǎn)擊目錄:File->new,創(chuàng )建一個(gè)可執行的ARM映象工程
ARMExecutableImage生成ELF格式映像(bin)
ARMObjectLibrary生成armar格式目標庫文件
EmptyProject創(chuàng )建不包含任何庫或源文件的工程
MakefileImporterWizard用于Vc
ThumbARMInterworkingImage用于A(yíng)RM和thumb指令混合代碼生成的ELF映像
ThumbExecutableimage用thumb指令生成ELF格式映像
ThumbObjectLibrary用于Thumb指令的代碼生成的armar格式文件
選ARMExecutableImage,工程文件名2440ART
加源文件
project->AddFiles
新建
填加已有
生成目標的配置
2440ART.mcp內雙擊
TargetSettins:post-linker選擇ArMfromELF
LanguageSettins:ArchitectureorProcessor選擇相應的編譯器ARM920T
ArmLinker:output內RO0x30000000
options內Imageentrypoint設為0x30000000
layout內Object2440init.oSectionInit
Listings內Imagemap
ArmfromELF:outputformat內Plainbinary
outputfilename內*.bin
編譯
make
調試AXD是調試器
設置,debug->打開(kāi)AXD調試界面,選擇option->configtarget選項
選ARMUL(模擬調試器),然后選擇確定.進(jìn)入調試界面.
ARMUL是虛擬調試環(huán)境(虛擬開(kāi)發(fā)板)
如果用開(kāi)發(fā)板真實(shí)環(huán)境調試,則需要使用JTAG連開(kāi)發(fā)板后,在此處選H-JTAG
用file- execute->runtocousor項.使程序進(jìn)入用戶(hù)主程序 可以用F8來(lái)一條一條執行語(yǔ)句,也可用F10,可以設置斷點(diǎn). (五).匯編語(yǔ)言基本結構 例: AREAInit,CODE,READONLY;AREA定義代碼段,段名Init;代碼段,只讀 ENTRY;偽操作,第一條指令的入口 Start;標號,一段代碼的開(kāi)始,用于標記,無(wú)意義,必須頂格 MOVr0,#10;將10存入r0寄存器,整型常量前面用#號 MOVr1,#3;將3存入r1寄存器,r0和r1相當于兩個(gè)變量,只是名稱(chēng)固定,在寄存器的存儲空間內 ADDr0,r0,r1;將r0內數據與r1內數據相加,相加后數據放在r0內 ;Stop;停止標號,下面的代碼用于停止運行程序 ;MOVr0,#0x18;軟件異常中斷響應 ;LDRr1,=0x20026;ADP停止運行,應用退出 ;SWI0x123456;ARM半主機軟件中斷 END 1).基本概念 (2)寄存器:如R0、R1等 ARM的匯編編程,本質(zhì)上就是針對CPU寄存器的編程。 (3)指令:即操作碼,直接控制CPU,如MOV 包括跳轉指令、數據處理指令、乘法指令、PSR訪(fǎng)問(wèn)指令、加載或存儲指令、數據交換指令、移位指令等 (4)偽操作:作用于編譯器,大多用于定義和控制。如AREA 包括符號定義、數據定義、控制等 (5)標號:僅是一種標識。在跳轉語(yǔ)句中,可以指向要跳轉到的標識號位置 在A(yíng)RM匯編中,標號代表一個(gè)地址,段內標號的地址在匯編時(shí)確定,段外標號的地址值在連接時(shí)確定 (6)符號:即標號(代表地址)、變量名、數字常量名等。符號的命名規則如下: a.符號由大小寫(xiě)字母、數字以及下劃線(xiàn)組成; b.除局部標號以數字開(kāi)頭外,其它的符號不能以數字開(kāi)頭; c.符號區分大小寫(xiě),且所有字符都是有意義的; d.符號在其作用域范圍你必須是唯一的; e.符號不能與系統內部或系統預定義的符號同名; f.符號不要與指令助記符、偽指令同名。 2).段定義 在匯編語(yǔ)言中,以相對獨立的指令或數據序列的程序段組成程序代碼 段的劃分:數據段、代碼段。一個(gè)匯編程序至少有一個(gè)代碼段 (1)代碼段 上面的例子為代碼段。 AREA定義一個(gè)段,并說(shuō)明所定義段的相關(guān)屬性,CODE用以指明為代碼段 ENTRY標識程序的入口點(diǎn)。END為程序結束。 (2)數據段 AREADATAAREA,DATA,BIINIT,ALLGN=2 DISPBUFSPACE200 RCVBUFSPACE200 DATA用以指明為數據段, SPACE分配200字節的存儲單元并初始化為0 指令和偽操作在后面詳細描述 3).匯編語(yǔ)言結構 [標號][指令或偽操作] 所有標號必須在一行的頂格書(shū)寫(xiě),其后不加冒號 所有指令均不能頂格寫(xiě) 指令助記符大小寫(xiě)敏感,不能大小寫(xiě)混合,只能全部大寫(xiě)或全部小寫(xiě) ;為注釋 @代碼行注釋?zhuān)? #整行注釋或直接操作數前綴 為換行符 ENTRY為程序的入口 END為程序的結束 //*arm體系結構第二天* 二、ARM的尋址方式 最簡(jiǎn)單的匯編指令格式是操作碼和操作數 如:MOVr0,#10 操作碼:即CPU指令如MOVADD 操作數:即表示數據是在寄存器中還是在內存中,是絕對地址還是相對地址 操作數部分要解決的問(wèn)題是,到哪里去獲取操作數,獲取操作數的方式就是尋址方式。 ARM每一條指令都是32位機器碼,對應CPU的位數 ARM指令格式: 在32位中分為7個(gè)位域,每個(gè)位域分別存儲不同意義的編碼(二進(jìn)制數) 31~2827~2524~212019~1615~1211~0 ----------------------------------------------------------- |Cond|001|Opcode|S|Rn|Rd|Operand2| ----------------------------------------------------------- 對應輸入的一條指令為: Opcode:指令操作碼用4個(gè)位存儲,如MOV,ADD每一個(gè)操作碼和操作數最終都是以二進(jìn)制形式存在 Cond:指令的條件碼用4個(gè)位來(lái)儲,可省略 如:ADDr0,r0,#1;計算r0加1后的值,再寫(xiě)入到r0內 ADDEQr0,r0,#1;只有在CPSR寄存器條件標志位滿(mǎn)足指定條件時(shí),才計算r0加1后的值,再寫(xiě)入到r0內 其中條件助記符如下: EQ相等 NE不相等 MI負數 VS溢出 PL正數或零 HI無(wú)符號大于 LS無(wú)符號小于或等于 CS無(wú)符號大于或等于 CC無(wú)符號小于 GT有符號大于 GE有符號大于或等于 LE有符號小于或等于 LT有符號小于 AL無(wú)條件執行 S:決定指令的操作是否影響CPSR的值,用一個(gè)位存儲,省略則表示為0值,否則為1值 如SUBSR0,R0,#1;R0減1,結果放入R0,同時(shí)響影CPSR的值 SUBR0,R0,#1;R0減1,結果放入R0,不影響CPSR的值 Rd:目標寄存器編碼用4位存儲 Rn:第1個(gè)操作數的寄存器編碼用4位存儲 Operand2:第2個(gè)操作數用12位存儲 尋址方式:是根據指令中給出的地址碼字段來(lái)實(shí)現尋找真實(shí)操作數地址的方式,共8種尋址方式: 寄存器尋址、立即尋址、寄存器間接尋址、基址尋址、多寄存器尋址、堆棧尋址、相對尋址、寄存器移位尋址 //*尋址方式是重點(diǎn)需要理解背訟* 1.立即尋址 操作數是常量,用#表示常量。例 MOVR0,#0xFF000;指令省略了第1個(gè)操作數寄存器。將立即數0xFF000(第2操作數)裝入R0寄存器 SUBR0,R0,#64;R0減64,結果放入R0 #表示立即數0x或&表示16進(jìn)制數否則表示十進(jìn)制數 立即數尋址指令中的地址碼就是操作數本身,可以立即使用的操作數。 其中,#0xFF000和#64都是立即數。該立即數位于32位機器碼中,占低位的12位。也就是說(shuō)在A(yíng)RM指令中以12位存儲一個(gè)數據 那么,32位的數據(long或float)如何存儲? 32位的數據以一種特殊的方式來(lái)處理 其中:高4位表示的無(wú)符號整數 低8位補0擴展為32位,然后循環(huán)右移x位來(lái)代表一個(gè)數。x=高4位整數*2 所以,不是每一個(gè)32位數都是合法的立即數,只有能通過(guò)上述構造得到的才是合法的立好數。如: 合法立即數:0xff,0x104,0xff0 不合法立即數:ox101,0x102,0xff1 2.寄存器尋址 操作數的值在寄存器中,指令執行時(shí)直接取出寄存器值來(lái)操作 例:MOVR1,R2;將R2的值存入R1在第1個(gè)操作數寄存器的位置存放R2編碼 SUBR0,R1,R2;將R1的值減去R2的值,結果保存到R0在第2操作數位置,存放的是寄存器R2的編碼 寄存器尋址是根據寄存器編碼獲取寄存器內存儲的操作數 3.寄存器間接尋址 操作數從寄存器所指向的內存中取出,寄存地存儲的是內存地址 例:LDRR1,[R2];將R2指向的存儲單元的數據讀出,保存在R1中R2相當于指針變量 STRR1,[R2];將R1的值寫(xiě)入到R2所指向的內存 SWPR1,R1,[R2];將寄存器R1的值和R2指定的存儲單元的內容交換 [R2]表示寄存器所指向的內存,相當于*p LDR指令用于讀取內存數據 STR指令用于寫(xiě)入內存數據 //----------------------------------------------------- 4.寄存器基址尋址 操作數從內存址偏移后讀取數據。常用于查表、數組操作、功能部件寄存器訪(fǎng)問(wèn)等。 基址:相當于首地址,地址偏移后取值作為操作數 基址寄存器:用來(lái)存放基址的寄存器 變址尋址:基址尋址也稱(chēng)為變址尋址 1)前索引尋址 例:LDRR2,[R3,#4];讀取R3+4地址上的存儲單元的內容,放入R2 LDRR2,[R3,#4]!;讀取R3+4地址上的存儲單元的內容,放入R2,然后R3內的地址變?yōu)镽3+4,即R3=R3+4 [R3,#4]表示地址偏移后取值,相當于*(p+4)或p[4],R3內保存的地址不變 [R3,#4]!表示地址偏移后取值,相當于*(p+4)或p[4],!表示回寫(xiě),即R3=R3-4,R3的值發(fā)生了改變 2)后索引尋址 例:LDRR2,[R3],#4;先讀取R3地址上的存儲單元的內容,放入R2,然后R3內的地址變?yōu)镽3+4,即R3=R3+4 [R3],#4類(lèi)似于*p++只不過(guò)自加的不是1,而是指定的4 [R3,#4]!類(lèi)似于*++p只不過(guò)自加的不是1,而是指定的4 3)寄存器的值作索引 例:LDRR2,[R3,R0];R0內存儲索引值,R3內存地址,讀取R3+R0地址上的存儲單元的內容,放入R2 [R3,R0]表示地址偏移后取值,相當于*(p+i)或p[i] 5.多寄存器尋址 一次可傳送多個(gè)寄存器的值,也稱(chēng)為塊拷貝尋址 例:LDMIAR1!,{R2-R7,R12};將R1指向的存儲單元中的數據讀寫(xiě)到R2~R7、R12中,然后R1自加1 STMIAR1!,{R2-R7,R12};將寄存器R2~R7、R12的值保存到R1指向的存儲單元中,然后R1自加1 其中R1是基址寄存器,用來(lái)存基址,R2-R7、R12用來(lái)存數據賦值編號小的寄存器與低地址相對應,與寄存器列表順序無(wú)關(guān) !為可選后綴,表示改變R1的值,則當數據傳送完畢之后,將最后的地址寫(xiě)入基址寄存器 基址寄存器不允許為R15,寄存器列表可以為R0~R15的任意組合。 這里R1沒(méi)有寫(xiě)成[R1]!,是因為這個(gè)位不是操作數位,而是寄存器位 LDMIA和STMIA是塊拷貝指令,LDMIA是從R1所指向的內存中讀數據,STMIA是向R1所指向的內存寫(xiě)入數據 R1指向的是連續地址空間 6.寄存器堆棧尋址 是按特定順序存取存儲區,按后進(jìn)先出原則,使用專(zhuān)門(mén)的寄存器SP(堆棧指針)指向一塊存儲區 例:LDMIASP!,{R2-R7,R12};將棧內的數據,讀寫(xiě)到R2~R7、R12中,然后下一個(gè)地址成為棧頂 STMIASP!,{R2-R7,R12};將寄存器R2~R7、R12的值保存到SP指向的棧中 SP指向的是棧頂 7.相對尋址 即讀取指令本身在內存中的地址。是相對于PC內指令地址偏移后的地址。 由程序計數器PC提供基準地址,指令中的地址碼字段作為偏移量,兩者相加后得到的地址即為操作數的有效地址。 例: BLOOP;B指令用于跳轉到標號LOOP指令處執行代碼 ... LOOP MOVR6#1 其中LOOP僅僅是標號,而不是地址,不是CPU指令,所以在指令的機器碼中沒(méi)有標號的機器碼,而是 計算出了從B指令到MOV指令之間內存地址的差值,這個(gè)差值相當于PC的偏移量,即相當于:addpc,pc,#偏移量 B指令引起了PC寄存器值的變化,PC內永遠保存將要運行指令在內存中的地址。 8.寄存器移位尋址 操作數在被使用前進(jìn)行移位運算 例:MOVR0,R2,LSL#3;R2的值左移3位,結果放入R0,;即是R0=R2×8 LSL是移位指令,用于將前面寄存器的數據左移 //^^^^^^^^^^^^^^^^^^^^^^^^^^下午^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ARM匯編語(yǔ)言語(yǔ)句由指令、偽指令、偽操作、宏指令組成 三.ARM指令集 共包括6種類(lèi)型:數據處理指令、跳轉指令、程序狀態(tài)指令、加載存取儲指令、協(xié)處理指令、軟中斷指令 (一)、數據處理指令 數據處理指令,只能對寄存器內容進(jìn)行操作,而不能對內存進(jìn)行操作,所有數據處理指令均可使用S后綴,并影響狀態(tài)標志 包括:數據傳送指令、算述指令、乘法指令、邏輯運算指令、比較指令、移位指令 1.數據傳輸指令 1)MOV數據傳送指令 格式:MOVRd, 功能:Rd=操作數,操作數可以是寄存器、被移位的寄存器或立即數。 例: MOVR0,#0xFF000;立即尋址,將立即數0xFF000(第2操作數)裝入R0寄存器 MOVR1,R2;寄存器尋址,將R2的值存入R1 MOVR0,R2,LSL#3;移位尋址,R2的值左移3位,結果放入R0 2)MVN數據取反傳送指令 格式:MVN 功能:將操作數傳送到目的寄存器Rd中,但該值在傳送前被按位取反,即Rd=!op1; 例: MVNR0,#0;R0=-1 mvn意為“取反傳輸”,它把源寄存器的每一位取反,將得到的結果寫(xiě)入結果寄存器。 movs和mvns指令對pc寄存器賦值時(shí)有特殊含義,表示要求在賦值的同時(shí)從spsr中恢復cpsr。 mov和mvn指令,編譯器會(huì )進(jìn)行智能的轉化。比如指令“movr1,0xffffff00”中的立即數是非法的。 在編譯時(shí),編譯器將其轉化為“mvnr1,0xff”,這樣就不違背立即數的要求。所以對于mov和mvn指令, 可以認為:合法的立即數反碼也是合法的立即數。 2.算術(shù)指令 1)ADD加法指令 格式:ADD{ 功能: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{ 功能:Rd=Rn+op2+carry,carry為進(jìn)位標志值。該指令用于實(shí)現超過(guò)32位的數的加法。 例如: 第一個(gè)64位操作數存放在寄存器R2,R3中; 第二個(gè)64位操作數存放在寄存器R4,R5中; 64位結果存放在R0,R1中。 ADDSR0,R2,R4;低32位相加,S表示結果影響條件標志位的值 ADCR1,R3,R5;高32位相加 3)SUB減法指令 格式:SUB{ 功能: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{ 功能:Rd=Rn-op2-!carry SUB和SBC生成進(jìn)位標志的方式不同于常規,如果需要借位則清除進(jìn)位標志,所以指令要對進(jìn)位標志進(jìn)行一個(gè)非操作。 例: 第一個(gè)64位操作數存放在寄存器R2,R3中; 第二個(gè)64位操作數存放在寄存器R4,R5中; 64位結果存放在R0,R1中。 SUBSR0,R2,R4;低32位相減,S表示結果影響條件標志位的值 SBCR1,R3,R5;高32位相減 5)其它減法指令 RSB反向減法指令,同SUB指令,但倒換了兩操作數的前后位置,即Rd=op2-Rn。 RSC帶借位的反向減法指令,同SBC指令,但倒換了兩操作數的前后位置,即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標志置1;否則置0。 subs和sbcs在產(chǎn)生借位時(shí)將cpsr的C標志置0;否則置1。 3.乘法指令 1)MUL32位乘法指令 格式:MUL{ 功能:Rd=Rn×op2 該指令根據S標志,決定操作是否影響CPSR的值;其中op2必須為寄存器。Rn和op2的值為32位的有符號數或無(wú)符號數。 例: MULSR0,R1,R2;R0=R1×R2,結果影響寄存器CPSR的值 2)MLA32位乘加指令 格式:MLA{ 功能:Rd=Rn×op2+op3op2和op3必須為寄存器。Rn、op2和op3的值為32位的有符號數或無(wú)符號數。 例: MLAR0,R1,R2,R3;R0=R1×R2+R3 3)SMULL64位有符號數乘法指令 格式:SMULL{ 功能:RdhRdl=Rn×op2Rdh、Rdl和op2均為寄存器。Rn和op2的值為32位的有符號數。 例: SMULLR0,R1,R2,R3;R0=R2×R3的低32位R1=R2×R3的高32位 4)SMLAL64位有符號數乘加指令 格式:SMLAL{ 功能:RdhRdl=Rn×op2+RdhRdl;Rdh、Rdl和op2均為寄存器。Rn和op2的值為32位的有符號數,RdhRdl的值為64位的加數。 例: SMLALR0,R1,R2,R3;R0=R2×R3的低32位+R0R1=R2×R3的高32位+R1 5)UMULL64位無(wú)符號數乘法指令 格式:UMULL{ 功能:同SMULL指令,但指令中Rn和op2的值為32位的無(wú)符號數。 例: UMULLR0,R1,R2,R3;R0=R2×R3的低32位R1=R2×R3的高32位其中R2,R3的值為無(wú)符號數 6)UMLAL64位無(wú)符號數乘加指令 格式:UMLAL{ 功能:同SMLAL指令,但指令中Rn,op2的值為32位的無(wú)符號數,RdhRdl的值為64位無(wú)符號數。 例: UMLALR0,R1,R2,R3;R0=R2×R3的低32位+R0R1=R2×R3的高32位+R1 ;其中R2,R3的值為32位無(wú)符號數R1,R0的值為64位無(wú)符號數 4.邏輯指令:and、orr、eor和bic 1)AND邏輯與指令 格式:AND{ 功能:Rd=RnANDop2一般用于清除Rn的特定幾位。 例: ANDR0,R0,#5;保持R0的第0位和第2位,其余位清0 2)ORR邏輯或指令 格式:ORR{ 功能:Rd=RnORop2一般用于設置Rn的特定幾位。 例: ORRR0,R0,#5;R0的第0位和第2位設置為1,其余位不變 3)EOR邏輯異或指令 格式:EOR{ 功能:Rd=RnEORop2一般用于將Rn的特定幾位取反。 例: EORR0,R0,#5;R0的第0位和第2位取反,其余位不變 4)BIC位清除指令 格式:BIC{ 功能:Rd=RnAND(!op2)用于清除寄存器Rn中的某些位,并把結果存放到目的寄存器Rd中 例: BICR0,R0,#5;R0中第0位和第2位清0,其余位不變 5.比較指令 1)CMP比較指令 格式:CMP{ 功能:Rn-op1,根據結果更新CPSR中條件標志位的值。 例: CMPR0,#5;計算R0-5,根據結果設置條件標志位 ADDGTR0,R0,#5;ADD為加法指令,GT為判斷條件標志位是否大于5,如果R0>5,則執行ADD指令 2)CMN反值比較指令 格式:CMN{ 功能:同CMP指令,但寄存器Rn的值是和op1取負的值進(jìn)行比較。 例: CMNR0,#5;把R0與-5進(jìn)行比較 3)TST位測試指令 格式:TST{ 功能:RnANDop1按位與后,更新CPSR中條件標志位,用于檢查寄存器Rn是否設置了op1中相應的位。 例: TSTR0,#5;測試R0中第0位和第2位是否為1 4)TEQ相等測試指令 格式:TEQ{ 功能:RnEORop1按位異或后,更新CPSR中條件標志位,用于檢查寄存器Rn的值是否和op1所表示的值相等。 例: TEQR0,#5;判斷R0的值是否和5相等 6.移位指令(位運算指令) 1)LSL(或ASL)左移 格式:寄存器,LSL(或ASL)操作數 功能:將寄存器內的數據左移,操作數是移位的位數在0-31之間 例: MOVR0,R1,LSL#2;將R1中的內容左移兩位后傳送到R0中。 2)LSR操作右移 格式:寄存器LSR操作數 功能:將寄存囂內的數據右移 例: MOVR0,R1,LSR#2;將R1中的內容右移兩位后傳送到R0中,左端用零來(lái)填充。 3)其它移位 ASR右移,左端用第31位值來(lái)填充 ROR右移,循環(huán)右移,左端用右端移出的位來(lái)填充 RRX右移,循環(huán)右移,左端用進(jìn)位標志位C來(lái)填充 //*arm體系結構第三天* (二)、跳轉指令 1)B跳轉指令 格式:B{ 功能:跳轉到addr地址。 例: Bexit;程序跳轉到標號exit處 2)BL帶返回的跳轉指令 格式:BL{ 功能:同B指令,但BL指令執行跳轉操作的同時(shí),還將PC(寄存器R15)的值保存到LR寄存器(寄存器R14)中。 該指令用于實(shí)現子程序調用,程序的返回可通過(guò)把LR寄存器的值到PC寄存器中來(lái)實(shí)現。 例: BLfunc;調用子程序func … func … MOVR15,R14;子程序返回 3)其它跳轉指令 BLX帶返回和狀態(tài)切換的跳轉指令,用于子程序調用和程序Thumb狀態(tài)的切換。 BX帶狀態(tài)切換的跳轉指令,處理器跳轉到目標地址處,目標地址處的指令可以是ARM指令,也可以是Thumb指令。 跳轉指令用于實(shí)現程序的跳轉和程序狀態(tài)的切換。 ARM程序設計中,實(shí)現程序跳轉有兩種方式:跳轉指令、直接向程序寄存器PC中寫(xiě)入目標地址值。 //---------------------------------------------------------------------------------------- (三)、程序狀態(tài)指令 用于狀態(tài)寄存器和通用寄存器間傳送數據??偣灿袃蓷l指令:MRS和MSR。兩者結合可用來(lái)修改程序狀態(tài)寄存器的值。 1)MRS程序狀態(tài)寄存器到通用寄存器的數據傳送指令 格式:MRS{ 功能:用于將程序狀態(tài)寄存器的內容傳送到目標寄存器Rd中。 例: MRSR0,CPSR;狀態(tài)寄存器CPSR的值存入寄存器R0中 2)MSR通用寄存器到程序狀態(tài)寄存器的數據傳送指令 格式:MSR{ 功能:用于將寄存器Rd的值傳送到程序狀態(tài)寄存器中。 32位的狀態(tài)寄存器可以分為4個(gè)域: 位[31:24]為條件標志位域,用f表示。 位[23:16]為狀態(tài)位域,用s表示。 位[15:8]為擴展位域,用x表示。 位[7:0]為控制位域,用c表示。 例: MSRCPSR_f,R0;用R0的值修改CPSR的條件標志域 MSRCPSR_fsxc,#5;CPSR的值修改為5 (四)、加載存儲指令 該集合的指令使用頻繁,當數據存放在內存中時(shí),必須先把數據從內存裝載到寄存器,執行完后再把寄存器 中的數據存儲到內存中 1.單數據訪(fǎng)存指令 1)單數據加載指令 格式:LDR(或LDRB、LDRBT、LDRH、LDRSB、LDRSH、LDRT、STR、STRB、STRBT、STRH、STRT) 功能:內存地址中的數據裝載到目標寄存器Rd中,同時(shí)還可以把合成的有效地址寫(xiě)回到基址寄存器。 尋址方式:Rn:基址寄存器。Rm:變址寄存器。Index:偏移量,12位的無(wú)符號數。 LDRRd,[Rn];把內存中地址為Rn的字數據裝入寄存器Rd中 LDRRd,[Rn,Rm];將內存中地址為Rn+Rm的字數據裝入寄存器Rd中 LDRRd,[Rn,#index];將內存中地址為Rn+index的字數據裝入Rd中 LDRRd,[Rn,Rm,LSL#5];將內存中地址為Rn+Rm×32的字數據裝入Rd LDRRd,[Rn,Rm]!;將內存中地址為Rn+Rm的字數據裝入Rd,并將新地址Rn+Rm寫(xiě)入Rn LDRRd,[Rn,#index]!;將內存中地址為Rn+index的字數據裝入Rd,并將新地址Rn+index寫(xiě)入Rn LDRRd,[Rn,Rm,LSL#5]!;將內存中地址為Rn+Rm×32的字數據裝入Rd,并將新地址Rn+Rm×32寫(xiě)入Rn LDRRd,[Rn],Rm;將內存中地址為Rn的字數據裝入寄存器Rd,并將新地址Rn+Rm寫(xiě)入Rn LDRRd,[Rn],#index;將內存中地址為Rn的字數據裝入寄存器Rd,并將新地址Rn+index寫(xiě)入Rn LDRRd,[Rn],Rm,LSL#5;將內存中地址為Rn的字數據裝入寄存器Rd,并將新地址Rn+Rm×32寫(xiě)入Rn 各指令的區別: (1)LDR字數據加載指令 將內存地址中的字數據裝載到目標寄存器Rd中 例:LDRR0,[R1,R2,LSL#5]!;將內存中地址為R1+R2×32的字數據裝入寄存器R0,并將新地址R1+R2×32寫(xiě)入R1 (2)LDRB字節數據加載指令 同LDR,只是從內存讀取一個(gè)8位的字節數據而不是一個(gè)32位的字數據,并將Rd的高24位清0。 例:LDRBR0,[R1];將內存中起始地址為R1的一個(gè)字節數據裝入R0中 (3)LDRBT用戶(hù)模式的字節數據加載指令 同LDRB指令,但無(wú)論處理器處于何種模式,都將該指令當作一般用戶(hù)模式下的內存操作。 (4)LDRH半字數據加載指令 同LDR指令,但該指令只是從內存讀取一個(gè)16位的半字數據而不是一個(gè)32位的字數據,并將Rd的高16位清0。 例:LDRHR0,[R1];將內存中起始地址為R1的一個(gè)半字數據裝入R0中 (5)LDRSB有符號的字節數據加載指令 同LDRB指令,但該指令將寄存器Rd的高24位設置成所裝載的字節數據符號位的值。 例:LDRSBR0,[R1];將內存中起始地址為R1的一個(gè)字節數據裝入R0中,R0的高24位設置成該字節數據的符號位 (6)LDRSH有符號的半字數據加載指令 同LDRH指令,但該指令將寄存器Rd的高16位設置成所裝載的半字數據符號位的值。 例:LDRSHR0,[R1];將內存中起始地址為R1的一個(gè)16位半字數據裝入R0中,R0的高16位設置成該半字數據的符號位 (7)LDRT用戶(hù)模式的字數據加載指令 同LDR指令,但無(wú)論處理器處于何種模式,都將該指令當作一般用戶(hù)模式下的內存操作。有效地址必須是字對齊的 2)單數據存儲指令 格式:STR(或STR、STRB、STRBT、STRH、STRT) 功能:將寄存器數據寫(xiě)入到內存中 尋址方式:Rn:基址寄存器。Rm:變址寄存器。Index:偏移量,12位的無(wú)符號數。 STRRd,[Rn];將寄存器Rd中的字數據寫(xiě)入到內存中地址為Rn內存中 STRRd,[Rn,Rm];將寄存器Rd中的字數據寫(xiě)入到內存中地址為Rn+Rm的內存中 STRRd,[Rn,#index];將寄存器Rd中的字數據寫(xiě)入到內存中地址為Rn+index內存中 STRRd,[Rn,Rm,LSL#5];將寄存器Rd中的字數據寫(xiě)入到內存中地址為Rn+Rm×32內存中 STRRd,[Rn,Rm]!;將寄存器Rd中的字數據寫(xiě)入到內存中地址為Rn+Rm的內存中 STRRd,[Rn,#index]!;將寄存器Rd中的字數據寫(xiě)入到內存中地址為Rn+index的內存中,并將新地址Rn+index寫(xiě)入Rn STRRd,[Rn,Rm,LSL#5]!;將寄存器Rd中的字數據寫(xiě)入到內存中地址為Rn+Rm×32的內存中,并將新地址Rn+Rm×32寫(xiě)入Rn STRRd,[Rn],Rm;將寄存器Rd中的字數據寫(xiě)入到內存中地址為Rn的內存中,并將新地址Rn+Rm寫(xiě)入Rn STRRd,[Rn],#index;將寄存器Rd中的字數據寫(xiě)入到內存中地址為Rn的內存中,并將新地址Rn+index寫(xiě)入Rn STRRd,[Rn],Rm,LSL#5;將寄存器Rd中的字數據寫(xiě)入到內存中地址為Rn的內存中,并將新地址Rn+Rm×32寫(xiě)入Rn (1)STR字數據存儲指令 把寄存器Rd中的字數據(32位)保存到addr所表示的內存地址中,同時(shí)還可以把合成的有效地址寫(xiě)回到基址寄存器。 例:STRR0,[R1,#5]!;把R0中的字數據保存到以R1+5為地址的內存中,然后R1=R1+5 (2)STRB字節數據存儲指令 把寄存器Rd中的低8位字節數據保存到addr所表示的內存地址中。 例:STRBR0,[R1];將寄存器R0中的低8位數據存入R1表示的內存地址中 (3)STRBT用戶(hù)模式的字節數據存儲指令 同STRB指令,但無(wú)論處理器處于何種模式,該指令都將被當作一般用戶(hù)模式下的內存操作。 (4)STRH半字數據存儲指令 把寄存器Rd中的低16位半字數據保存到addr所表示的內存地址中,而且addr所表示的地址必須是半字對齊的。 例:STRHR0,[R1];將寄存器R0中的低16位數據存入R1表示的內存地址中 (5)STRT用戶(hù)模式的字數據存儲指令 同STR指令,但無(wú)論處理器處于何種模式,該指令都將被當作一般用戶(hù)模式下的內存操作。 2.多數據訪(fǎng)存指令 1)批量數據加載指令 格式:LDM{ 功能:從一片連續的內存單元讀取數據到各個(gè)寄存器中,內存單元的起始地址為基址寄存器Rn的值,各個(gè)寄存器由寄存 器列表regs表示。 該指令一般用于多個(gè)寄存器數據的出棧。 type字段種類(lèi): IA:每次傳送后地址加1。 IB:每次傳送前地址加1。 DA:每次傳送后地址減1。 DB:每次傳送前地址減1。 FD:滿(mǎn)遞減堆棧。 ED:空遞減堆棧。 FA:滿(mǎn)遞增堆棧。 EA:空遞增堆棧。 堆棧尋址的命令LDMFA/STMFA、LDMEA/STMEA、LDMFD/STMFD、LDMED/STMED。 LDM和STM表示多寄存器尋址,即一次可以傳送多個(gè)寄存器值。 LDM:一次裝載多個(gè),這里用來(lái)出棧。 STM:一次存儲多個(gè),這里用來(lái)入棧。 F/E表示指針指向的位置 F:full滿(mǎn)堆棧,表示堆棧指針指向最后一個(gè)入棧的有效數據項。 E:empty空堆棧,表示堆棧指針指向下一個(gè)要放入的空地址。 A/D表示堆棧的生長(cháng)方式 A:堆棧向高地址生長(cháng),即遞增堆棧。 D:堆棧向低地址生長(cháng),即遞減堆棧。 注意:有一個(gè)約定,編號低的寄存器在存儲數據或者加載數據時(shí)對應于存儲器的低地址。 FD、ED、FA和EA指定是滿(mǎn)棧還是空棧,是升序棧還是降序棧,用于堆棧尋址。 一個(gè)滿(mǎn)棧的棧指針指向上次寫(xiě)的最后一個(gè)數據單元. 空棧的棧指針指向第一個(gè)空閑單元。 一個(gè)降序棧是在內存中反向增長(cháng)而升序棧在內存中正向增長(cháng)。 {!}:若選用了此后綴,則當指令執行完畢后,將最后的地址寫(xiě)入基址寄存器。 {^}:當regs中不包含PC時(shí),該后綴用于指示指令所用的寄存器為用戶(hù)模式下的寄存器, 否則指示指令執行時(shí),將寄存器SPSR的值到CPSR中。 2)批量數據存儲指令 格式:STM{ 功能:將各個(gè)寄存器的值存入一片連續的內存單元中,內存單元的起始地址為基址寄存器Rn的值 各個(gè)寄存器由寄存器列表regs表示。該指令一般用于多個(gè)寄存器數據的入棧。 {^}:指示指令所用的寄存器為用戶(hù)模式下的寄存器。其他參數用法同LDM指令。 例:STMEAR13!,{R0-R12,PC};將寄存器R0~R12以及程序計數器PC的值保存到R13指示的堆棧中 3.數據交換指令 1)字數據交換指令 格式:SWP 功能:Rd=[op2],[op2]=op1 從op2所表示的內存裝載一個(gè)字并把這個(gè)字放置到目的寄存器Rd中,然后把寄存器op1的內容存儲到同一內存地址中。 例:SWPR0,R1,[R2];將R2所表示的內存單元中的字數據裝載到R0,然后將R1中的字數據保存到R2所表示的內存單元中 2)字節數據交換指令 格式:SWPB 功能:從op2所表示的內存裝載一個(gè)字節并把這個(gè)字節放置到目的寄存器Rd的低8位中,Rd的高24位設置為0; 然后將寄存器op1的低8位數據存儲到同一內存地址中。 例:SWPBR0,R1,[R2];將R2所表示的內存單元中的一個(gè)字節數據裝載到R0的低8位,然后將R1中的低8位字節 數據保存到R2所表示的內存單元中 //^^^^^^^^^^^^^^^^^^^^^^^^^^下午^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (五)、協(xié)處理指令 1)CDP協(xié)處理器操作指令 格式:CDP{ , 功能:用于傳遞指令給協(xié)處理器p,要求其在寄存器CRn和CRm上,進(jìn)行操作opcode1,并把結果存放到CRd中, 可以使用opcode2提供與操作有關(guān)的補充信息。指令中的所有寄存器均為協(xié)處理器的寄存器,操作由協(xié)處理器完成。 指令中: P為協(xié)處理器編號; CRd為目的寄存器的協(xié)處理器寄存器; CRm和CRn為存放操作數的協(xié)處理器寄存器; Opcode1和opcode2為協(xié)處理器即將執行的操作。 例:CDPp5,5,c0,c1,c2,9;該指令用于通知協(xié)處理器p5,在c1和c2上執行操作5和9,并將結果存放到c0中。 2)LDC協(xié)處理器數據讀取指令 格式:LDC{ , 功能:將addr表示的內存地址中的連續數據傳送到目的寄存器CRd中。 L表示指令為長(cháng)讀取操作,比如用于雙精度數據的傳輸; 目的寄存器CRd為協(xié)處理器的寄存器; addr的尋址方式同LDR指令,其寄存器為ARM處理器的寄存器。 例:LDCp5,c1,[R1+5]:該指令用于將R1+5所對應的存儲單元中的數據,傳送到協(xié)處理器p5的寄存器c1中。 3)STC協(xié)處理器數據存儲指令 格式:STC{ , 功能:將寄存器CRd的值傳送到addr表示的內存地址中。指令中各參數用法同LDC。 例如:STCp5,c1,[R1+5];該指令用于將協(xié)處理器p5中寄存器c1的數據傳送到R1+5所對應的存儲單元中。 4)MCRARM寄存器到協(xié)處理器寄存器的數據傳送指令 格式:MCR{ , 功能:將ARM處理器的寄存器Rd中的數據傳送到協(xié)處理器p的寄存器CRn,CRm中;op1,op2為協(xié)處理器將要執行的操作。 例:MCRp5,5,R1,C1,C2,9;該指令將R1中的數據傳送到協(xié)處理器p5的寄存器C1,C2中,協(xié)處理器執行操作5和9。 MRC協(xié)處理器寄存器到ARM寄存器的數據傳送指令 格式:MRC{ , 功能:將協(xié)處理器p的寄存器CRn,CRm的數據傳送到ARM處理器的寄存器Rd中;op1,op2為協(xié)處理器將要執行的操作。 例:MRCp5,5,R1,C1,C2,9;該指令將寄存器C1,C2中的數據傳送到R1中,協(xié)處理器p5協(xié)處理器執行操作5和9。 (六)、異常中斷指令 異常中斷產(chǎn)生指令:用于系統調用和調試。 1)SWI軟件中斷指令 格式:SWI{ 功能:用于產(chǎn)生軟件中斷,以使用戶(hù)程序調用操作系統的系統例程。 指令中24位的立即數指定用戶(hù)程序調用系統例程的類(lèi)型,其參數通過(guò)通用寄存器傳遞。當24位的立即數 被忽略時(shí),系統例程類(lèi)型由寄存器R0指定,其參數通過(guò)其他通用寄存器傳遞。 例:SWI0X05;調用編號為05的系統例程。 2)BKPT斷點(diǎn)中斷指令 格式:BKPT16位的立即數; 功能:用于產(chǎn)生軟件斷點(diǎn)中斷,以便軟件調試時(shí)使用。16位的立即數用于保存軟件調試中額外的斷點(diǎn)信息。 指令操作的偽代碼: (七)、信號量操作指令 信號量操作指令:用于進(jìn)程間的同步互斥,提供對信號量的原子操作。 (八)、ARM程序常見(jiàn)結構 1.子函數和主函數 使用BL指令進(jìn)行調用,該指令會(huì )把返回的PC值保存在LR AREAExample,CODE,READONLY;聲明代碼段Example ENTRY;程序入口 Start MOVR0,#0;設置實(shí)參,將傳遞給子程騙子的實(shí)參存放在r0和r1內 MOVR1,#10 BLADD_SUM;調用子程序ADD_SUM BOVER;跳轉到OVER標號處,進(jìn)入結尾 ADD_SUM ADDR0,R0,R1;實(shí)現兩數相加 MOVPC,LR;子程序返回,R0內為返回的結果 OVER END 運行過(guò)程 (1)程序從ENTRY后面指令處開(kāi)始運行(即主函數) R0和R1先準備好數據令子函數運算時(shí)使用 (2)BL為跳轉指令,用于調用子函數后面為函數標號。 在調用函數的過(guò)程中,自動(dòng)將PC內的值保存到LR(R14)內備份,PC內當前的值為下一條要執行的指令地址 (即BOVER指令地址),在子函數結束前將該地址恢復到PC內 (3)B為跳轉指令,用于跳轉到指定的標號后,此處跳轉到程序結尾 (4)MOVPC,LR是子函數內的最后一條語(yǔ)句,用于將LR內保存的地址恢復到PC內 PC(R15)程序計數器存儲要執行的指令在內存中的地址 PC的值=當前正在執行指令在內存中的地址+8 2.條件跳轉語(yǔ)句 AREAExample,CODE,READONLY;聲明代碼段Example ENTRY;程序入口 Start MOVR0,#2;將R0賦初值2 MOVR1,#5;將R1賦初值5 ADDR5,R0,R1;將R0和R1內的值相加并存入R5 CMPR5,#10 BEQDOEQUAL;若R5為10,則跳轉到DOEQUAL標簽處 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,并設置相應標志位 BNEWAIT;若R1的d7位為1則跳轉到WAIT標簽 OVER END 運行過(guò)程 (1)程序從ENTRY后面指令處開(kāi)始運行(即主函數) R0和R1賦初值2和5 將R0與R1相加后存入R5內 (2)CMP用于比較兩個(gè)數據,指令格式如下: CMP操作數1,操作數2 CMP用于把一個(gè)寄存器的內容和另一個(gè)寄存器或立即數進(jìn)行比較,同時(shí) 更新CPSR中條件標志位的值。標志位表示操作數1和操作數2的關(guān)系。然后執行后面的語(yǔ)句 (3)條件助記符 BEQB為跳轉指令,EQ為條件相等,讀取CPSR內的條件標志位,如相等則跳轉到所指定的標號處 BNEB為跳轉指令,NE為不相等(0),如不相等則跳轉到所指定的標號處 ADDHIADD為相加指令,HI為無(wú)符號大于,如大于則執行相加 ADDLSADD為相加指令,LS為無(wú)符號小于或等于,如小于或等于則相加 (4)位運算 ANDSAND按位與,0x80取出第7位,S為將該位與的值寫(xiě)入標志位 3.循環(huán)語(yǔ)句 AREAExample,CODE,READONLY;聲明代碼段Example ENTRY;程序入口 Start MOVR1,#0;將R1賦初值0 LOOP ADDR1,R1,#1 CMPR1,#10 BCCLOOP;R1小于10則執行跳轉到LOOP處執行循環(huán),即R1從0到10后退出循環(huán) END 例:編寫(xiě)一具有完整匯編格式的程序,實(shí)現冒泡法排序功能。 設無(wú)符號字數據存放在從0x400004開(kāi)始的區域,字數據的數目字存放在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 練習: 1.編寫(xiě)1+2+3+…+100的匯編程序。 2.實(shí)現子函數,該函數返回兩個(gè)參數中的最大值,在主函數內調用。 //*arm體系結構第四天* 四.偽操作和宏指令 偽指令——是匯編語(yǔ)言程序里的特殊指令助記符,在匯編時(shí)被合適的機器指令替代。 偽操作——為匯編程序所用,在源程序進(jìn)行匯編時(shí)由匯編程序處理,只在匯編過(guò)程起作用,不參與程序運行。 宏指令——通過(guò)偽操作定義的一段獨立的代碼。在調用它時(shí)將宏體插入到源程序中。也就是常說(shuō)的宏。 說(shuō)明:所有的偽指令、偽操作和宏指令,均與具體的開(kāi)發(fā)工具中的編譯器有關(guān) 1.宏定義(MACRO、MEND) 格式:MACRO {$標號名}宏名{$參數1,$參數2,……} 指令序列 MEND MACRO、MEND偽指令可以將一段代碼定義為一個(gè)整體,稱(chēng)為宏指令,在程序中通過(guò)宏指令多次調用該段代碼。 {}為可選項 $標號在宏指令被展開(kāi)時(shí),標號會(huì )被替換為用戶(hù)定義的符號 在宏定義體的第一行應聲明宏的原型(包含宏名、所需的參數),然后就可以在匯編程序中通過(guò)宏名來(lái)調用該指令序列 寫(xiě)在代碼段或數據段前面 MEXIT跳出宏 例:沒(méi)有參數的宏(實(shí)現子函數返回) MACRO MOV_PC_LR;宏名 MOVPC,LR;子程序返回,R0內為返回的結果 MEND AREAExample,CODE,READONLY;聲明代碼段Example ENTRY;程序入口 Start MOVR0,#0;設置實(shí)參,將傳遞給子程騙子的實(shí)參存放在r0和r1內 MOVR1,#10 BLADD_NUM;調用子程序ADD_NUM BLSUB_NUM;調用子程序SUB_NUM BOVER;跳轉到OVER標號處,進(jìn)入結尾 EXPORTADD_NUM ADD_NUM ADDR0,R0,R1;實(shí)現兩數相加 MOV_PC_LR;調用宏,代表子函數結束 EXPORTSUB_NUM SUB_NUM SUBR0,R1,R0;實(shí)現兩數相減 MOV_PC_LR;調用宏,代表子函數結束 OVER END 例:有參數宏 宏定義從MACRO偽指令開(kāi)始,到MEND結束,并可以使用參數。類(lèi)似于C的#define MACRO;宏定義 CALL$Function,$dat1,$dat2;宏名稱(chēng)為CALL,帶3個(gè)參數 IMPORT$Function;聲明外部子程序宏開(kāi)始 MOVR0,$dat1;設置子程序參數,R0=$dat1 MOVR1,$dat2 BL$Function;調用子程序宏最后一句 MEND;宏定義結束 CALLFADD1,#3,#2;宏調用,后面是三個(gè)參數 匯編預處理后,宏調用將被展開(kāi),程序清單如下: IMPORTFADD1 MOVR0,#3 MOVR1,#3 BLFADD1 2.符號定義偽操作 1)定義常量(EQU) 格式:標號名稱(chēng)EQU表達式{,類(lèi)型} 用于為程序中的常量、標號等定義一個(gè)等效的字符名稱(chēng),類(lèi)似于C語(yǔ)言中的#define。 其中EQU可用*代替。 標號名稱(chēng):為常量名。 表達式:寄存器的地址值、程序中的標號、32位地址常量、32位常量 當表達式為32位的常量時(shí),可以指定表達式的數據類(lèi)型,可以有以下三種類(lèi)型: CODE16、CODE32和DATA 示例:EQU的使用 XEQU45;即#defineX45,必須頂格 YEQU64 stack_topEQU0x30200000,CODE32 AREAExample,CODE,READONLY CODE32 ENTRY Start LDRSP,=stack_top;stack_top內的值0x30200000是地址,=stack_top是取stack_top常量地址(即指針的指針) MOVR0,#X;將X替換為45 STRR0,[SP];將R0內的45存入到SP所指向的內存中(SP此時(shí)是指針的指針) MOVR0,#Y LDRR1,[SP];從內存中讀取數據到R1內 ADDR0,R0,R1 STRR0,[SP] END 注:X,Y,stack_top為標號,必須頂格寫(xiě),大多寫(xiě)在代碼段外面 2)定義變量 常量:數字常量,有三種表示方式:十進(jìn)制數、十六進(jìn)制數、字符串常量、布爾常量(如testnoSETS{FALSE}) 變量:數字變量、邏輯變量、字符串變量 (1)*GBLA、GBLL、GBLS定義全局變量 格式:GBLA(GBLL、GBLS)全局變量名 GBLA偽指令用于定義一個(gè)全局的數字變量,并初始化為0; GBLL偽指令用于定義一個(gè)全局的邏輯變量,并初始化為F(假); GBLS偽指令用于定義一個(gè)全局的字符串變量,并初始化為空; 由于以上三條偽指令用于定義全局變量,因此在整個(gè)程序范圍內變量名必須唯一。 示例:全局變量的定義與賦值 GBLAcount;定義全局變量 countSETA2;給全局變量賦值為2,必須頂格 AREAExample,CODE,READONLY CODE32 ENTRY Start MOVR0,#count;將count內的值寫(xiě)入R0內 ADDR0,R0,#2 BStart END 注:在賦值過(guò)程中,全局變量名必須頂格寫(xiě),全局變量常在代碼段外定義和賦值 示例:變量與內存地址 GBLAglobv globvSETA23 AREAExample,CODE,READONLY;聲明代碼段Example ENTRY;程序入口 Start LDRR0,=globv;golbv是全局變量,將內存地址讀入到R0內 LDRR1,[R0];將內存數據值讀入到R1內 ADDR1,R1,#2 STRR1,[R0];將修改后數據再賦給變量 MOVR0,#0 OVER END 注:#取變量值=取變量地址[R0]讀取R0內地址所指向的數據值 (2)*LCLA、LCLL、LCLS定義局部變量 格式:LCLA(LCLL或LCLS)局部變量名 LCLA偽指令用于定義一個(gè)局部的數字變量,并初始化為0; LCLL偽指令用于定義一個(gè)局部的邏輯變量,并初始化為F(假); LCLS偽指令用于定義一個(gè)局部的字符串變量,并初始化為空; 以上三條偽指令必須寫(xiě)在宏定義內,用于聲明局部變量,宏結束,局部變量不再起作用 示例: LCLAnum;聲明一個(gè)局部的數字變量,變量名為num numSETA0xaa;將該變量賦值為0xaa LCLLisOk;聲明一個(gè)局部的邏輯變量,變量名為isOk isOkSETL;將該變量賦值為真 LCLSstr1;定義一個(gè)局部的字符串變量,變量名為str1 str1SETS"Testing";將該變量賦值為"Testing" 示例:局部變量的定義與賦值 MACRO MOV_START;宏名 LCLAx;定義局部變量 LCLAy xSETA12;必須頂格寫(xiě) ySETA24 MOVR0,#2 MOVR1,#3 ADDR0,R0,R1 MEND AREAExample,CODE,READONLY;聲明代碼段Example ENTRY;程序入口 Start MOV_START MOVR0,#0 OVER END 注:在賦值過(guò)程中,局部變量名必須頂格寫(xiě),局部變量必須在宏定義內使用 (3)*SETA、SETL和SETS用于給一個(gè)已經(jīng)定義的全局變量或局部變量賦值。 SETA偽指令用于給一個(gè)數學(xué)變量賦值; SETL偽指令用于給一個(gè)邏輯變量賦值; SETS偽指令用于給一個(gè)字符串變量賦值; 其中,變量名為已經(jīng)定義過(guò)的全局變量或局部變量,表達式為將要賦給變量的值。 (4)變量代換$ $在數字變量前,將變值轉換為十六進(jìn)制字符串 $在邏輯變量前,將變量轉換為真或假 $在字符串變量前,替換后面變量的字符串 如: LCLSY1;定義局部字符串變量Y1和Y2 LCLSY2 Y1SETS"WORLD!" Y2SETS"LELLO,$Y1";將字符串Y2的值替換$Y1,形成新的字符串 3)、定義一個(gè)寄存器(RN) 格式:名稱(chēng)RN表達式 RN偽指令用于給一個(gè)寄存器定義一個(gè)別名。 示例: TempRNR0;將R0定義一個(gè)別名Temp 4)定義寄存器列表(RLIST) 格式:名稱(chēng)RLIST{寄存器列表} 用于對一個(gè)通用寄存器列表定義名稱(chēng),使用該偽指令定義的名稱(chēng)可在A(yíng)RM指令LDM/STM中使用。 在LDM/STM指令中,寄存器列表中的寄存器訪(fǎng)問(wèn)次序總是先訪(fǎng)問(wèn)編號較低的寄存器,再訪(fǎng)問(wèn)編號較高的寄存器,而不管寄存器列表中各寄存器的排列順序。 示例:RegListRLIST{R0-R5,R8,R10};將寄存器列表名稱(chēng)定義為RegList,用于多寄存器尋址(后面詳解) 5)定義協(xié)處理器寄存器(CN) 格式:名稱(chēng)CN協(xié)處理器的寄存器編號 示例:PowerCN6;將協(xié)處理器的寄存器6名稱(chēng)定義為Power 6)定義協(xié)處理器(CP) 格式:名稱(chēng)CP協(xié)處理器名 示例:DmuCP6;將協(xié)處理器6名稱(chēng)定義為Dmu 7)定義浮點(diǎn)或精度寄存器(DN,SN,FN) 格式:名稱(chēng)DN雙精度寄存器編號;DN為雙精度VFP寄存器定義名稱(chēng) 格式:名稱(chēng)SN單精度寄存器編號;SN為單精度VFP寄存器定義名稱(chēng) 格式:名稱(chēng)FN浮點(diǎn)寄存器編號;FN為浮點(diǎn)寄存器定義名稱(chēng) 示例: heightDN6;將VFP雙精度寄存器6名稱(chēng)定義為height widthSN16;將VFP單精度寄存器16名稱(chēng)定義為width heightFN6;將浮點(diǎn)寄存器6名稱(chēng)定義為height //^^^^^^^^^^^^^^^^^^^^^^^^^^下午^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3.數據定義偽操作(申請內存) 數據定義偽指令用于為特定的數據分配存儲單元,同時(shí)可完成已分配存儲單元的初始化。 1)按類(lèi)型分配內存 格式:標號偽指令表達式 標號 表達式:初始化的值,表達式可以為程序標號或字符、數字表達式 偽指令:如下 (1)DCB用于分配一片連續的字節存儲單元(字符數組),可用=號代替 StrDCB"Thisisatest!";分配一片連續的字節存儲單元并初始化。 (2)DCW(或DCWU)用于分配一片連續的半字存儲單元(16位短整型數組),DCW半字對齊,DCWU不嚴格半字對齊。 DataTestDCW1,2,3 (3)DCD(或DCDU)用于分配一片連續的字存儲單元(32位,整型數組),DCD可用&代替,DCD字對齊的 DataTestDCD4,5,6 (4)DCFD(或DCFDU)為雙精度的浮點(diǎn)數分配一片連續的字存儲單元,每個(gè)雙精度的浮點(diǎn)數占據兩個(gè)字單元。 FDataTestDCFD2E115,-5E7 (5)DCFS(或DCFSU)為單精度的浮點(diǎn)數分配一片連續的字存儲單元,每個(gè)單精度的浮點(diǎn)數占據一個(gè)字單元。 FDataTestDCFS2E5,-5E-7 (6)DCQ(或DCQU)用于分配一片以8個(gè)字節為單位的連續存儲區域(每8字節為一個(gè)數據的數組) DataTestDCQ100;分配一片連續的存儲單元并初始化為指定的值100。 2)申請連續內存 (1)申請一個(gè)連續內存(SPACE) 用于分配一片連續的存儲區域并初始化為0,可用%代替 格式:標號SPACE表達式 表達式為要分配的字節數 例:DataSpaceSPACE100;分配連續100字節的存儲單元并初始化為0。 (2)聲明一個(gè)數據緩沖池的開(kāi)始(LTORG) 通常,把數據緩沖池放在代碼段的最后面,下一個(gè)代碼段之前,或END之前 示例: AREAExample,CODE,READONLY;聲明代碼段Example ENTRY;程序入口 Start BLfuncl funcl LDRR0,=0x12345678 ADDR1,R1,R0 MOVPC,LR LTORG;定義緩沖池0x12345678LTORG根據LDR確定內存地址 dataSPACE4200;從當前位置開(kāi)始分配4200字節內存 END (3)定義一個(gè)結構化的內存表首地址(MAP) 格式:MAP表達式{,基址寄存器} 用于定義一個(gè)結構化的內存表的首地址??捎胇代替。 表達式可以為程序中的標號或數學(xué)表達式,基址寄存器為可選項. 當基址寄存器選項不存在時(shí),表達式的值即為內存表的首地址, 當該選項存在時(shí),內存表的首地址為表達式的值與基址寄存器的和。 例: DatastrucSPACE280;分配280個(gè)字節單元 MAPDatastruc;內存表的首地址為Datastruc內存塊 (4)定義一個(gè)結構化內存表的數據域(FILED) 用于定義一個(gè)結構化內存表中的數據域??捎?代替 格式:標號FILED表達式 FIELD偽指令常與MAP偽指令配合使用來(lái)定義結構化的內存表。表達式的值為當前數據域所占的字節數。 標號為數據域(字段、成員變量)名 MAP偽指令定義內存表的首地址, FIELD偽指令定義內存表中的各個(gè)數據域,并可以為每個(gè)數據域指定一個(gè)標號供其他的指令引用。(3)內存首地址(MAP) MAP偽指令通常與FIELD偽指令配合使用來(lái)定義結構化的內存表。 示例: DatastrucSPACE280;分配280個(gè)字節單元 MAPDatastruc;內存表的首地址為Datastruc內存塊 constaFIELD4;字段consta長(cháng)度4字節,相對地址0 constabFIELD4;字段constab長(cháng)度4字節,相對地址4 xFIELD8;字段x長(cháng)度8字節,相對地址8 yFIELD8;字段y長(cháng)度8字節,相對地址16 stringFIELD256;字段string長(cháng)度256字節,相對地址24 LDRR6,[R9,consta];引用內存表中的數據域 注意:MAP偽操作和FIELD偽操作僅僅是定義數據結構,他們并不實(shí)際分配內存單元,而SPACE用于分配內存 4.匯編控制偽操作 用于控制匯編程序的執行流程,常用的匯編控制偽指令包括以下幾條: (1)IF邏輯表達式...ELSE...ENDIF條件控制 (2)WHILE邏輯表達式...WEND循環(huán)控制 例:條件編譯 AREAExample,CODE,READONLY CODE32 Data_in*100;定義標號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è)全局的數學(xué)變量,變量名為Counter CounterSETA3;由變量Counter控制循環(huán)次數 …… WHILECounter<10 指令序列 IFcontinue MEXIT;退出宏 ENDIF WEND 5.其他常用的偽指令 1)、AREA 格式:AREA段名屬性1,屬性2,…… AREA偽指令用于定義一個(gè)代碼段或數據段。其中,段名若以數字開(kāi)頭,則該段名需用"|"括起來(lái),如|1_test|。 屬性字段表示該代碼段(或數據段)的相關(guān)屬性,多個(gè)屬性用逗號? —CODE屬性:用于定義代碼段,默認為READONLY。 —DATA屬性:用于定義數據段,默認為READWRITE。 —READONLY屬性:指定本段為只讀,代碼段默認為READONLY。 —READWRITE屬性:指定本段為可讀可寫(xiě),數據段的默認屬性為READWRITE。 —ALIGN屬性:使用方式為ALIGN表達式。在默認時(shí),ELF(可執行連接文件)的代碼段和數據段是按字對齊的 —COMMON屬性:定義一個(gè)通用的段,不包含任何的用戶(hù)代碼和數據。各源文件中同名的COMMON段共享同一段存儲單元 一個(gè)匯編語(yǔ)言程序至少要包含一個(gè)段,當程序太長(cháng)時(shí),也可以將程序分為多個(gè)代碼段和數據段。 使用示例: AREAInit,CODE,READONLY 該偽指令定義了一個(gè)代碼段,段名為Init,屬性為只讀 2)、ENTRY 格式:ENTRY 用于指定匯編程序的入口點(diǎn)。一個(gè)源文件里最多只能有一個(gè)ENTRY(可以沒(méi)有)。 3)、END 格式:END 用于通知編譯器已經(jīng)到了源程序的結尾。 4)、CODE16、CODE32 格式:CODE16(或CODE32) CODE16偽指令通知編譯器,其后的指令序列為16位的Thumb指令。 CODE32偽指令通知編譯器,其后的指令序列為32位的ARM指令。 在使用ARM指令和Thumb指令混合編程的代碼里,可用這兩條偽指令進(jìn)行切換 示例: AREAInit,CODE,READONLY CODE32;通知編譯器其后的指令為32位的ARM指令 LDRR0,=NEXT+1;將跳轉地址放入寄存器R0 BXR0;程序跳轉到新的位置執行,并將處理器切換到Thumb工作狀態(tài) …… CODE16;通知編譯器其后的指令為16位的Thumb指令 NEXTLDRR3,=0x3FF …… END;程序結束 5)、EXPORT(或GLOBAL) 格式:EXPORT標號 export偽指令用于在程序中聲明一個(gè)全局的標號,該標號可在其他的文件中引用。 6)、IMPORT 格式:IMPORT標號 IMPORT偽指令用于通知編譯器要使用的標號在其他的源文件中定義,但要在當前源文件中引用 如果當前源文件實(shí)際并未引用該標號,該標號也會(huì )被加入到當前源文件的符號表中。 7)、EXTERN 格式:EXTERN標號 EXTERN偽指令用于通知編譯器要使用的標號在其他的源文件中定義,但要在當前源文件中引用 如果當前源文件實(shí)際并未引用該標號,該標號就不會(huì )被加入到當前源文件的符號表中。 8)、GET(或INCLUDE) 格式:GET文件名 GET偽指令用于將一個(gè)源文件包含到當前的源文件中,并將被包含的源文件在當前位置進(jìn)行匯編處理。 匯編程序中常用的方法是在某源文件中定義一些宏指令,用EQU定義常量的符號名稱(chēng),用MAP和FIELD定義結構化的數據類(lèi)型,然后用GET偽指令將這個(gè)源文件包含到其他的源文件中。使用方法與C語(yǔ)言中的"include"相似。 GET偽指令只能用于包含源文件,包含目標文件需要使用INCBIN偽指令 示例: AREAInit,CODE,READONLY GETa1.s;通知編譯器當前源文件包含源文件a1.s GETC:a2.s;通知編譯器當前源文件包含源文件C:a2.s…… END 9)、INCBIN 格式:INCBIN文件名 INCBIN偽指令用于將一個(gè)目標文件或數據文件包含到當前的源文件中 示例: AREAInit,CODE,READONLY INCBINa1.dat;通知編譯器當前源文件包含文件a1.dat INCBINC:a2.txt;通知編譯器當前源文件包含文件C:a2.txt…… END 10)、ROUT 格式:{名稱(chēng)}ROUT ROUT偽指令用于給一個(gè)局部變量定義作用范圍。 在程序中未使用該偽指令時(shí),局部變量的作用范圍為所在的AREA, 而使用ROUT后,局部變量的作為范圍為當前ROUT和下一個(gè)ROUT之間。 11)、ALIGN 格式:ALIGN{表達式{,偏移量}} ALIGN偽指令可通過(guò)添加填充字節的方式,使當前位置滿(mǎn)足一定的對其方式 表達式的值用于指定對齊方式,可能的取值為2的冪,如1、2、4、8、16等。 偏移量也為一個(gè)數字表達式,若使用該字段,則當前位置的對齊方式為:2的表達式次冪+偏移量。 示例: AREAInit,CODE,READONLY,ALIEN=3;指定后面的指令為8字節對齊。 五.ARM匯編偽指令(讀取內存地址) 1)ADR及ADRL 將PC相對偏移的地址或基于寄存器相對偏移的地址值讀取到寄存器中 格式:ADR(ADRL)寄存器,地址表達式 ADR小范圍的地址讀取偽指令, ADRL中等范圍的地址讀取偽指令 例: 查表 ADRR0,D_TAB;加載轉換表地址 LDRBR1,[R0,R2];使用R2作為參數,進(jìn)行查表 …… D_TAB DCB0xC0,0xF9,0xA4,0xB0,0x99,0x92 2)LDR 用于加載32位立即數或一個(gè)地址值到指定的寄存器,大范圍的地址讀取偽指令. LDR通常都是作加載指令,但是它也可以作偽指令。作用是裝載一個(gè)32bit常數和一個(gè)地址到寄存器。 格式:LDR寄存器,=地址表達式 COUNTEQU0x56000054;COUNT是一個(gè)變量,地址為0x56000054。 LDRR1,=COUNT;將COUNT這個(gè)變量的值(地址),也就是0x56000054放到R1中。 MOVR0,#0 STRR0,[R1];是一個(gè)典型的存儲指令,將R0中的值放到以R1中的值為地址的存儲單元去 ;這三條指令是為了完成對變量COUNT賦值。 3)NOP 空操作偽指令,可用于延時(shí)操作 例:延時(shí)子程序 Delay NOP;空操作 NOP NOP SUBSR1,R1,#1;循環(huán)次數減1 BNEDelay MOVPC,LR 六、Thumb指令集 有興趣的自學(xué) //*arm體系結構第五天* 七、C語(yǔ)言與匯編混合編程 完全使用匯編語(yǔ)言來(lái)編寫(xiě)程序會(huì )非常的繁瑣 通常,只是使用匯編程序來(lái)完成少量必須由匯編程序才能完成的工作,而其它工作則由C語(yǔ)言程序來(lái)完成。 (一)、ATPCS規則 混合編程中,雙方都須遵守ATPCS規則。這些基本規則包括: 子程序調用過(guò)程中寄存器的使用規則、數據棧的使用規則和參數的傳遞規則。 1.寄存器使用規則 寄存器:R4-R11用來(lái)保存局部變量 R0-R3(a1-a4)用于保存參數/返回結果/scratch(臨時(shí)寄存器) R4-R11(v1-v8)用于保存ARM狀態(tài)局部變量 R12(IP)子程序內部調用的scratch R13(SP)數據棧指針 R14(LR)連接寄存器 R15(PC)程序計數器 R7又可稱(chēng)為wr用于Thumb狀態(tài)工作寄存器 R9又可稱(chēng)為sb在支持RWPI的ATPCS中為靜態(tài)基址寄存器 R10又可稱(chēng)為sl在支持PWPI的ATPCS中為數據棧限制指針 R11又可稱(chēng)為fp用于幀指針 2.數據棧使用規則 ATPCS標準規定,數據棧為FD(滿(mǎn)遞減類(lèi)型),并且對數據棧的操作是8字節對齊。在進(jìn)行出棧和入棧操作,則 必須使用ldmfd和strnfd指令(或ldmia和stmdb) 3.參數的傳遞規則 參數:參數小于等于4,用R0-R3保存參數,參數多于4,剩余的傳入堆棧 函數返回:結果為32位整數,通過(guò)R0返回 結果為64位整數,通過(guò)R0,R1返回 對于位數更多的結果,通過(guò)內存傳遞 例:參數傳遞及結果返回(r0-r3做參數,r0做返回值) AREAExample,CODE,READONLY;聲明代碼段Example ENTRY;程序入口 Start MOVR3,#4;設置實(shí)參,將參數寫(xiě)入R0-R3 MOVR2,#3 MOVR1,#2 MOVR0,#1 BLfunc1;調用子程序ADD_SUM BOVER;跳轉到OVER標號處,進(jìn)入結尾 func1 ADDR0,R0,R1;實(shí)現兩數相加 ADDR0,R0,R2 ADDR0,R0,R3 MOVPC,LR;子程序返回,R0內為返回的結果 OVER END 相當于如下C語(yǔ)言 intfunc1(inta,intb,intc,intd){ returna+b+c+d; } intmain(){ func1(1,2,3,4); } 例:多于4個(gè)參數,前4個(gè)通過(guò)寄存器R0-R3傳遞,其它參數通過(guò)數據棧傳遞 AREAExample,CODE,READONLY;聲明代碼段Example ENTRY;程序入口 Start STMFDSP!,{R1-R4,LR};先將R1-R4,及LR內原有數據壓入棧,需要使用這五個(gè)寄存器 MOVR0,#1;準備好7個(gè)寄存囂存入7個(gè)數據LR,IP,R4作臨時(shí)寄存器使用 MOVIP,#2 MOVLR,#3 MOVR4,#4 MOVR1,#5 MOVR2,#6 MOVR3,#7 STMFDSP!,{R1-R3};先將R1-R3數據從前向后入棧,然后將IP,LR,R4內的數據裝入R1-R3 MOVR3,R4;其中IP,LR,R4是臨時(shí)使用的寄存器 MOVR2,LR MOVR1,IP BLfunc1;調用子程序funclR0是返回結果 LDMFDSP!,{R1-R4,PC};從棧中取出最初的數據,恢復原始值 BOVER;跳轉到OVER標號處,進(jìn)入結尾 func1 STMFDSP!,{R4,LR};當調用函數時(shí),LR和R4都已發(fā)生了變化,其中LR是指令地址所以也壓入棧 LDRR4,[SP,#0x10];目前共壓入5個(gè)數據,每一個(gè)數據占兩個(gè)字節,當前棧頂偏移10為前5個(gè)數據7 ADDLR,SP,#8;將前第4個(gè)數據的地址(棧項+偏移)賦給LR LDMIALR,{IP,LR};連續將LR地址處的兩個(gè)數據取出寫(xiě)入IP和LR內,從右向左寫(xiě),LDMIA即出棧指令 ADDR0,R0,R1;從此行開(kāi)始相當于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};從棧內取數據加載入R4和PC,PC跳轉回主函數 OVER END 下面是棧頂 相當于如下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匯編程序間的相互調用 1.匯編程序調用C子程序 為保證程序調用時(shí)參數正確傳遞,必須遵守ATPCS。 在C程序中函數不能定義為static函數。在匯編程序中需要在匯編語(yǔ)言中使用IMPORT偽操作來(lái)聲明C子函數 //C代碼 intsum5(inta,intb,intc,intd) { return(a+b+c+d); } //匯編代碼 AREAExample,CODE,READONLY;聲明代碼段Example IMPORTsum5; ENTRY;程序入口 Start MOVR3,#4;設置實(shí)參,將參數寫(xiě)入R0-R3 MOVR2,#3 MOVR1,#2 MOVR0,#1 BLsum5;調用子程序sum5 BOVER;跳轉到OVER標號處,進(jìn)入結尾 OVER END 2.匯編程序訪(fǎng)問(wèn)全局C變量 匯編程序中可以通過(guò)C全局變量的地址來(lái)間接訪(fǎng)問(wèn)C語(yǔ)言中定義的全局變量 在編編程序中用IMPORT引入C全局變量,該C全局變量的名稱(chēng)在匯編程序中被認為是一個(gè)標號。通過(guò)ldr和str指令訪(fǎng)問(wèn)該編號所代表的地址 //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內 MOVR0,#2 ADDR0,R0,R1 STRR0,i;將寄存器值寫(xiě)入i內 MOVR3,#4;設置實(shí)參,將參數寫(xiě)入R0-R3 MOVR2,#3 MOVR1,#2 MOVR0,#1 BLsum5;調用子程序ADD_SUM BOVER;跳轉到OVER標號處,進(jìn)入結尾 OVER END 3.在C語(yǔ)言中調用匯編子程序 為保證程序調用時(shí)參數的正確傳遞,在匯編程序中需要使用EXPORT偽操作來(lái)聲明匯編子程序,同時(shí)在C語(yǔ)言中使用extern擴展聲明匯編子程序。 //匯編代碼 EXPORTfunc1;func1為子函數名 AREAExample,CODE,READONLY;聲明代碼段Example func1 ADDR0,R0,R1;實(shí)現兩數相加 ADDR0,R0,R2 ADDR0,R0,R3 MOVPC,LR;子程序返回,R0內為返回的結果 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ǔ)言中調用匯編全局變量 匯編中用DCD為全局變量分配空間并賦值,并定義一個(gè)標號代表該存儲位置。 在匯編中用EXPORT導出標號(這個(gè)標號就是全局變量),在C程序中用extern擴展聲明名該變量 //匯編代碼 EXPORTfunc1 EXPORTtmp AREAExample,CODE,READONLY;聲明代碼段Example tmp;全局變量名 DCD0x0005;全局變量創(chuàng )建內存空間及賦初值 func1;子函數名 ADDR0,R0,R1;實(shí)現兩數相加 ADDR0,R0,R2 ADDR0,R0,R3 MOVPC,LR;子程序返回,R0內為返回的結果 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ǔ)言中內嵌匯編 有些操作C語(yǔ)言程序無(wú)法實(shí)現,如改變CPSP寄存器值,初始化堆棧指針寄存器SP等,這些只能由匯編來(lái)完成。 但出于編程簡(jiǎn)潔等一些因素,有時(shí)需要在C源代碼中實(shí)現上述操作,此時(shí)就需要在C中嵌入少量匯編代碼。 內嵌的匯編不能直接引用C的變量定義,必須通過(guò)ATPCS進(jìn)行,語(yǔ)法格式如下: __asm{ //內嵌匯編 } 例:在C語(yǔ)言中嵌入匯編 intf(){//C函數 __asm{//內嵌匯編,禁用中斷例子 MRSR0,CPSR ORRR0,R0,#0x80 MSRCPSR_c,R0 } } intmain(intargc,charargv){ inta; intz=f(a); printf("%d",z); return0; } 出地完整性考慮,內嵌匯編相對于一般匯編的不同特點(diǎn)如下: 1)操作數可以是寄存器、常量或C表達式??梢允莄har、short、或int類(lèi)型,而且是無(wú)符號數進(jìn)行操作 2)常量前的#號可以省略 3)只有指令B可以使用C程序中的標號,指令BL不可以使用 4)不支持匯編語(yǔ)言中用于內存分配的偽操作 5)內嵌匯編不支持通過(guò)“.”指示符或PC獲取當前指令地址 6)不支持LDRRn,=expression偽指令,而使用MOVRn,expression指令向寄存器賦值 7)不支持標號表達式 8)不支持ADR和ADRL偽指令 9)不支持BX和BLX指令 10)不可以向PC賦值 11)使用0x前綴替代&表示十六進(jìn)制數 12)不使用寄存尋址變量 13)ldm和stm指令的寄存器列表只允許物理寄存器 14)必須小心使用物理寄存器,如R0-R3,LR和PC //------------------------------------------ (三)、裸機硬件的控制方法 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填加配置選項 將Flash.csv到安裝目錄的ETCJFlash內 打開(kāi)菜單options->projectsettings 在彈出對話(huà)框內選flash后,點(diǎn)擊按鈕selectflashdevice 在彈出對話(huà)框內選擇EN29LV160AB 6.連接開(kāi)發(fā)板 重啟開(kāi)發(fā)板,然后點(diǎn)擊菜單target->connect查看聯(lián)接信息 (3)燒寫(xiě)方法 將j-link連接好后,在菜單file->open內選擇要燒寫(xiě)的程序。 如:燒寫(xiě)u-boot.bin, 然后在燒寫(xiě)地址對話(huà)框內輸入燒寫(xiě)地址,u-boot的地址設為0 再點(diǎn)擊菜單target->program進(jìn)行燒寫(xiě) (4)調試方法 連接JLINK和開(kāi)發(fā)板。 打開(kāi)程序J-LINKARMV4.08i->J-linkGDBServer 設置信息JTAGspeed為500KH 所有選項勾選 設置AXD調試環(huán)境options->configuretarget填加JlinkARM目錄下的JLinkRDI.dll 然后在A(yíng)XD內選則JLinkRDI.dll選項,同時(shí)點(diǎn)擊右側configure按鈕 在彈出對話(huà)框內General標簽:JTAGspeed設為4000Khz flash標簽:去掉Enableflashprogramming選項 Breakpoints:去掉Usesoftwarebreakpoints選項 CPU標簽:勾選Allowinstructionsetsimulation Littleendian Resetstrategy內選 Hardware,haltafterreset(normal)和1000ms 在A(yíng)XD內,通過(guò)File->LoadImage載入要調試的axf文件 (四)、軟件控制硬件的編程原理 每一種硬件,在其控制器芯片上都有物理寄存器(不是CPU寄存器,是硬件上的寄存器) 這些寄存器分為三類(lèi):命令寄存器、狀態(tài)寄存器、數據寄存器 程序控制硬件的方法是,通過(guò)匯編str指令向命令寄存器寫(xiě)入指令即可完成對硬件的配置操作或執行物理操作。 通過(guò)匯編ldr指令從數據寄存器中獲取數據,或從狀態(tài)寄存器獲取狀態(tài)。 程序控制硬件,其實(shí)質(zhì)就是對硬件的寄存器進(jìn)行讀寫(xiě)操作。 程序中需要解決的問(wèn)題: 1)硬件寄存器的內存地址是多少? 2)向哪一個(gè)寄存器寫(xiě)入什么值? 如想解決上述兩個(gè)問(wèn)題,需要熟練查看硬件的手冊,閱讀硬件連線(xiàn)原理圖。 數電的基本知識: 1).電路符號 2).基本原理 以L(fǎng)ED燈為例講解如何使用硬件手冊,和原理圖 在開(kāi)發(fā)板上有4個(gè)LED燈,如果想點(diǎn)亮,必須先看硬件連線(xiàn)圖 1.查看開(kāi)發(fā)板LED管腳連線(xiàn) TQ2400開(kāi)發(fā)板有兩塊電路板:核心板、主板 LED燈在主板上,所以查看《TQ2440底板原理圖.pdf》,找到LED模塊(LED測試) 從電路圖可看到如果讓LED1燈點(diǎn)亮,必須在nLED_1連線(xiàn)上輸出低電屏(即通電) 如何讓LED1上輸出低電屏,需查看CPU相關(guān)引腳連線(xiàn) 2.查看TQ2440核心板原理圖 CPU在核心板上,所以查看《TQ2440_V2核心板原理圖.pdf》 找到nLED1連線(xiàn),如下 nLED1在CUP的GPB5引腳上 nLED2GPB6 nLED3GPB7 nLED4GPB8 如何讓CPU的GPB5為低電屏,需查看CPU引腳模式 3.查看CPU用戶(hù)手冊 TQ2440的微處理器芯片為S3C2440,所以查看《S3C2440.pdf》芯片手冊,找到GPB5的管腳模式 1)控制寄存器 GPB5[11:10]00=Input01=Output 10=nXBACK11=Reserved GPBCON控制寄存器 GPBDAT數據寄存器 GPBUP上拉使能寄存器 [11:10]是所在位,每個(gè)管腳都是占兩個(gè)位 其中Input是輸入值 Output輸出值 reserved保留值 nXBACKnXBACK/GPB5 即,如果將GPB5設為低電屏,則需要將控制寄存器設置為輸出模式。 即需要CPU寄存器GPBCON的11和10位設置為01即11位為010位為1 2)數據寄存器 GPB[10:0][10:0]當端口配置為輸入端口時(shí),相應位為引腳狀態(tài)。 當端口配置為輸出端口時(shí),相應位為引腳狀態(tài)。 當端口配置為功能引腳時(shí),將讀取到未定義值 所以,當GPBCON為輸入狀態(tài)時(shí),GPBDAT的相應位5則應為0才會(huì )輸出低電屏 3)查看寄存器地址 GPBCON0x56000010R/W端口配置寄存器復位值0x0 GPBDAT0x56000014R/W端口數據寄存器復位值- 所以,需要將地址位0x56000010的寄存器作為GPBCON寄存器 地址位0x56000014的寄存器作為GPBDAT寄存器 4.代碼編寫(xiě) //ledtest.c #defineGPBCON(*(volatileunsignedlong*)0x56000010) #defineGPBDAT(*(volatileunsignedlong*)0x56000014) //volatile影響編譯器編譯的結果,表示變量是隨時(shí)可能發(fā)生變化的 //0x56000010是地址,強行轉換為unsignedlong*類(lèi)型, //然后前面加*代表這個(gè)地址所指向的寄存器變量 #defineLEDS(1<<5|1<<6|1<<7|1<<8)//即11110000 #defineDELAYVAL(0xFFFF) externintdelay(inttime);//聲明匯編函數 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;比較的結果不為0,則繼續調用delay movPC,LR;返回 END 5.調試代碼 2440ART.mcp內雙擊 TargetSettins:post-linker選擇ArMfromELF LanguageSettins:ArchitectureorProcessor選擇相應的編譯器ARM920T ArmLinker:output內RO0x30000000 options內Imageentrypoint設為0x30000000 layout內Object2440init.oSectionInit Listings內Imagemap ArmfromELF:outputformat內Plainbinary outputfilename內*.bin 編譯 make 調試AXD是調試器 設置,debug->打開(kāi)AXD調試界面,選擇option->configtarget選項 選ARMUL(模擬調試器),然后選擇確定.進(jìn)入調試界面. ARMUL是虛擬調試環(huán)境(虛擬開(kāi)發(fā)板) 如果用開(kāi)發(fā)板真實(shí)環(huán)境調試,則需要使用JTAG連開(kāi)發(fā)板后,在此處選H-JTAG 用file- execute->runtocousor項.使程序進(jìn)入用戶(hù)主程序 可以用F8來(lái)一條一條執行語(yǔ)句,也可用F10,可以設置斷點(diǎn).
評論