ARM9彙編指令總結
1、 LDR指令
Arm指令集中,LDR既可以做爲加載指令,也可以作爲僞指令。
1) LDR pc, =MyHandleIRQ ;表示將MyHandleIRQ符號放入pc寄存器中
eg:
COUNT EQU 0x40003100
……
LDR R1,=COUNT
MOV R0,#0
STR R0,[R1]
COUNT是我們定義的一個變量,地址爲0x40003100。這中定義方法在彙編語言中是很常見的,如果使用過單片機的話,應該都熟悉這種用法。
LDR R1,=COUNT是將COUNT這個變量的地址,也就是0x40003100放到R1中。
MOV R0,#0是將立即數0放到R0中。最後一句STR R0,[R1]是一個典型的存儲指令,將R0中的值放到以R1中的值爲地址的存儲單元去。實際就是將0放到地址爲0x40003100的存儲單元中去。可見這三條指令是爲了完成對變量COUNT賦值。用三條指令來完成對一個變量的賦值,看起來有點不太舒服。這可能跟ARM的採用RISC有關。
2) LDR PC,MyHandleIRQ ;表示將讀取存儲器中MyHandleIRQ符號所表示的地址中的值,及需要多讀一次存儲器。
eg:
;將COUNT的值賦給R0
LDR R1,=COUNT
LDR R0,[R1] ;此爲加載指令
LDR R1,=COUNT這條僞指令,是怎樣完成將COUNT的地址賦給R1,有興趣的可以看它編譯後的結果。這條指令實際上會編譯成一條LDR指令和一條DCD僞指令。
3)、認真閱讀下列代碼,體會LDR的兩種用法
在代碼中:
start:
ldr pc,=MyHandleReset ;jump to HandleReset
ldr pc,=MyHandleUndef ;jump to HandleUndef
ldr pc,=MyHandleSWI ;jump to HandleSWI
ldr pc,=MyHandleIabort ;jump to HandleIabort
ldr pc,=MyHandleDabort ;jump to HandleDabort
nop
ldr pc,=MyHandleIRQ ;jump to HandleIRQ <=之前出錯的一行
ldr pc,=MyHandleFIQ ;jump to HandleFIQ
;MyHandleIRQ: .word OS_CPU_IRQ_ISR
MyHandleIRQ:
sub lr, lr, #4 ; to calculate the return address
stmdb sp!, {r0-r12,lr}
ldr lr, =int_return ; restore the return address
ldr pc, =int_handle ; call for the interrupt handler
在“之前出錯的一行”處,如果改成“ldr pc,MyHandleIRQ”當中斷來臨時,無法進行中斷處理。
另一種情況是正確的,注意體會:
start:
ldr pc,=MyHandleReset ;jump to HandleReset
ldr pc,=MyHandleUndef ;jump to HandleUndef
ldr pc,=MyHandleSWI ;jump to HandleSWI
ldr pc,=MyHandleIabort ;jump to HandleIabort
ldr pc,=MyHandleDabort ;jump to HandleDabort
nop
ldr pc,MyHandleIRQ ;jump to HandleIRQ <=之前出錯的一行
ldr pc,=MyHandleFIQ ;jump to HandleFIQ
MyHandleIRQ: .word OS_CPU_IRQ_ISR
;MyHandleIRQ:
; sub lr, lr, #4 ; to calculate
the return address
; stmdb sp!, {r0-r12,lr}
; ldr lr, =int_return ; restore the
return address
; ldr pc, =int_handle ; call for the
interrupt handler
因爲當中斷來臨時,還需要去MyHandleIRQ處把OS_CPU_IRQ_ISR取出,即多取一次存儲器。
2、 STR指令
STR Rd , addressing ;[addressing]←Rd (store)
STR r0, [r1, #0x10] ;r1+0x10這個是所用的實際地址值,但是不會寫入r1,在此句之後,r1值不變
STR r0, [r1], #0x10 ;r1+0x10這個是所用的實際地址值,這個值會寫入r1,此句之後,r1=r1+0x10
STR用來存取內存,從rom到ram,而mov是ram裏用的
3、 LDM指令和STM指令
批量加載/存儲指令可以實現在一組寄存器和一塊連續的內存單元之間傳輸數據。LDM爲加載多個寄存器,STM爲存儲多個寄存器。允許一條指令傳送16個寄存器的任何子集或所有寄存器。指令格式如下:
LDM{cond}<模式>
Rn{!},reglist{^}
STM{cond}<模式> Rn{!},reglist{^}
LDM /STM 的主要用途是現場保護、數據複製、參數傳送等。其模式有8種,如下所列:(前面4種用於數據塊的傳輸,後面4種是堆棧操作)。
(1) IA:每次傳送後地址加4
(2) IB:每次傳送前地址加4
(3) DA:每次傳送後地址減4
(4) DB:每次傳送前地址減4
(5) FD:滿遞減堆棧
(6) ED:空遞增堆棧
(7) FA:滿遞增堆棧
(8) EA:空遞增堆棧
其中,寄存器Rn爲基址寄存器,裝有傳送數據的初始地址,Rn不允許爲R15;後綴“!”表示最後的地址寫回到Rn中;寄存器列表reglist可包含多於一個寄存器或寄存器範圍,使用“,”分開,如{R1,R2,R6-R9},寄存器排列由小到大排列;“^”後綴不允許在用戶模式呈系統模式下使用,若在LDM指令用寄存器列表中包含有PC時使用,那麼除了正常的多寄存器傳送外,將SPSR拷貝到CPSR中,這可用於異常處理返回;使用“^”後綴進行數據傳送且寄存器列表不包含PC時,加載/存儲的是用戶模式的寄存器,而不是當前模式的寄存器。
地址對準――這些指令忽略地址的位[1:0]。在進行數據複製時,先設置好源數據指針,然後使用塊拷貝尋址指令LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA、LDMDB
/STMDB 進行讀取和存儲。而進行堆棧操作時,則要先設置堆棧指針,一般使用SP然後使用堆棧尋址指令STMFD/LDMFD、STMED。LDMED、STMFA/LDMFA、STMEA/LDMEA實現堆棧操作。
在堆棧請求格式中,FD,ED,FA,EA定義了前/後向索引和上/下位,F,E表示堆棧滿或者空。A和D定義堆棧是遞增還是遞減,如果遞增,STM將向上,LDM向下,如果遞減,則相反。當LDM/STM沒有用於堆棧,而只是簡單地表示地址前向增加,後向增加,前向減少,後向減少時,由IA,IB,DA,DB控制。
寄存器的任意子集或者所有的寄存器都可以被指定,唯一的限制是寄存器列表不應該爲空。任何時候R15被存儲到MEM中,存儲的值時指令地址加12。傳輸地址是由Rn中的內容和前/後向索引位,上/下位決定的,寄存器的傳輸按照從低向高的順序。如果寄存器列表中有R15,則R15在最後一個被傳輸。序號低得寄存器對應於存儲器的低地址。
詳解
1)、STMFD SP!,{R0,R1,R2,R14} ;滿遞減入棧,R13爲基址地址
效果圖如下:
2)、LDMFD SP!,{R0,R1,R2,R14} ;滿遞減出棧,R13爲基址地址
效果圖:
3)、STMED SP!,{R0,R1,R2,R14} ;滿遞減入棧,R13爲基址地址
效果圖:
4)、LDMED SP! , {R0,R1,R2,R14} ;空遞減出棧,R13爲基址地址
效果圖:
5)STMFA SP!,{R0,R1,R2,R14} ;滿遞增入棧,R13爲基址地址
效果圖:
6)、LDMFA SP!,{R0,R1,R2,R14} ;滿遞增出棧,R13爲基址地址
效果圖:
7)、STMEA SP!,{R0,R1,R2,R14} ;空遞增入棧,R13爲基址地址
效果圖
8)、LDMEA SP!,{R0,R1,R2,R14} ;空遞增出棧,R13爲基址地址
效果圖
9)、STMIA R0!,{R1,R2,R3,R14} ;先傳後增,寄存器 to RAM
效果圖
10)、LDMIA R0!,{R1,R2,R3,R14} ; 先傳後增,RAM to寄存器
效果圖
11)、STMIB R0!,{R0,R1,R2,R14} ;先增後傳,寄存器 to RAM
效果圖:
12)、LDMIB R0!, {R1,R2,R3,R14} ;先增後傳,RAM to寄存器
效果圖:
13)、STMDA R0! , {R1,R2,R3,R14} ;先傳後減,寄存器 to RAM
效果圖:
14)、LDMDA RO!,{R1,R2,R3,R14} ;先傳後減,RAM to寄存器
效果圖:
15)、STMDB R0!,{R1,R2,R3,R14} ;先減後傳,寄存器 to RAM
效果圖
16)、LDMDB R0! , {R1,R2,R3,R14} ;先減後傳, RAM to寄存器
效果圖:
使用LDM/STM進行數據複製例程如下:
…
LDR R0,=SrcData ;設置源數據地址
LDR R1,=DstData ;設置目標地址
LDMIA R0,{R2-R9} ;加載8字數據到寄存器R2~R9
STMIA R1,{R2-R9} ;存儲寄存器R2~R9到目標地址
使用LDM/STM進行現場寄存器保護,常在子程序中或異常處理使用:
SENDBYTE
STMFD SP!,{R0-R7,LR} ;寄存器入堆
…
BL DELAY ;調用DELAY子程序
…
LDMFD SP!,{R0-R7,PC} ;恢復寄存器,並返回
4、B、BL、BX、BLX和BXJ指令
跳轉、帶鏈接跳轉、跳轉並切換指令集、帶鏈接跳轉並切換指令集、跳轉並轉換到 Jazelle 狀態。
B或BL指令引起處理器轉移到“子程序名”處開始執行。兩者的不同之處在於BL指令在轉移到子程序執行之前,將其下一條指令的地址拷貝到R14(LR,鏈接寄存器)。由於BL指令保存了下條指令的地址,因此使用指令“MOV PC ,LR”即可實現子程序的返回。而B指令則無法實現子程序的返回,只能實現單純的跳轉。用戶在編程的時候,可根據具體應用選用合適的子程序調用語句。
BX 和 BLX 指令可將處理器的狀態從 ARM 更改爲 Thumb,或從 Thumb 更改爲 ARM。
BLX label 無論何種情況,始終會更改處理器的狀態。
BX Rm 和 BLX Rm 可從 Rm 的位 [0] 推算出目標狀態:如果 Rm 的位 [0] 爲 0,則處理器的狀態會更改爲(或保持在)ARM 狀態
如果 Rm 的位 [0] 爲 1,則處理器的狀態會更改爲(或保持在)Thumb 狀態。
BXJ 指令會將處理器的狀態更改爲 Jazelle。
通過 B BL BLX BX 可以完成在當前指令向前或者向後32MB的地址空間的跳轉,機器級指令 B 和 BL 對當前指令有地址範圍限制。 但是,即使label 超出範圍,仍可以使用這些指令。通常您並不知道鏈接器會將 label 放在何處。必要時鏈接器會添加代碼,以允許進行更長的跳轉。 所添加的代碼稱爲中間代碼。
爲什麼跳轉範圍是32MB呢?這要從B、BL的機器碼格式說起。
Cond | 1 | 0 | 1 | L | offset |
[31:28]位是條件碼;[27:24]位爲“1010”時,表示B跳轉指令,爲“1011”時,表示BL跳轉指令;[23:0]表示偏移地址。使用B或BL跳轉時,下一條指令的地址是這樣計算的:將指令中24位帶符號的補碼立即數擴展爲32(擴展其符號位);將此32位數左移兩位;將得到的值加到pc寄存器中,即得到跳轉的目標地址。既由於偏移地址爲24位所以,跳轉範圍爲32M。
BL的經典用法如下:
bl NEXT ; 跳轉到NEXT
……
NEXT
……
mov pc, lr ; 從子程序返回。