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