ARM單片機寄存器列表:
堆棧指針 R13
R13 是堆棧指針。在 CM3 處理器內核中共有兩個堆棧指針,於是也就支持兩個堆棧。
當引用 R13(或寫作 SP)時,你引用到的是當前正在使用的那一個,另一個必須用特殊的指
令來訪問(MRS,MSR 指令)。這兩個堆棧指針分別是:
主堆棧指針(MSP),或寫作 SP_main。這是缺省的堆棧指針,它由 OS 內核、異常服務
例程以及所有需要特權訪問的應用程序代碼來使用。
進程堆棧指針(PSP),或寫作 SP_process。用於常規的應用程序代碼(不處於異常服
用例程中時)。MSP和PSP 的含義是Main_Stack_Pointer 和Process_Stack_Pointer
堆棧的 PUSH 與 POP
堆棧是一種存儲器的使用模型。它由一塊連續的內存,以及一個棧頂指針組成,用
於實現“先進後出”的緩衝區。其最典型的應用,就是在數據處理前先保存寄存器的值,
再在處理任務完成後從中恢復先前保護的這些值。因此,在 PUSH 新數據時,堆棧指針先減一個單元。通常在進入一個子程序後,第一件事就是把寄存器的值先
PUSH 入堆棧中,在子程序退出前再 POP 曾經 PUSH 的那些寄存器。另外,PUSH 和 POP 還
能一次操作多個寄存器。可以使用 SP 表示 R13。在程序代碼中,both MSP 和 PSP 都被稱爲 R13/SP。
不過,我們可以通過 MRS/MSR 指令來指名道姓地訪問具體的堆棧指針。
MSP,亦寫作 SP_main,這是復位後缺省使用堆棧指針,服務於操作系統內核和異常服
務例程;而 PSP,亦寫作 SP_process,典型地用於普通的用戶線程中。寄存器的 PUSH 和 POP 操作永遠都是 4 字節對齊的——也就是說他們的地址必須是
0x4,0x8,0xc,……。這樣一來,R13的最低兩位被硬線連接到0,並且總是讀出0 (Read As Zero)。
連接寄存器 R14
R14 是連接寄存器(LR)。在一個彙編程序中,你可以把它寫作 both LR 和 R14。LR 用於
在調用子程序時存儲返回地址。例如,當你在使用 BL(分支並連接,Branch and Link)指令時,
就自動填充 LR 的值。main ;主程序
…
BL function1 ; 使用“分支並連接”指令呼叫 function1
; PC= function1,並且 LR=main 的下一條指令地址
…
Function1
… ; function1 的代碼
BX LR ; 函數返回(如果 function1 要使用 LR,必須在使用前 PUSH,
; 否則返回時程序就可能跑飛了——譯註)
程序計數器 R15
R15 是程序計數器,在彙編代碼中你也可以使用名字“PC”來訪問它。因爲 CM3 內部
使用了指令流水線,讀 PC 時返回的值是當前指令的地址+4。比如說:
0x1000: MOV R0, PC ; R0 = 0x1004
如果向 PC 中寫數據,就會引起一次程序的分支(但是不更新 LR 寄存器) )。CM3 中的指
令至少是半字對齊的,所以 PC 的 LSB 總是讀回 0。然而,在分支時,無論是直接寫 PC 的值
還是使用分支指令,都必須保證加載到 PC 的數值是奇數(即 LSB=1),用以表明這是在
Thumb 狀態(半字對齊)下執行。倘若寫了 0,則視爲企圖轉入 ARM 模式,CM3 將產生一個 fault 異
常。
特殊功能寄存器組
Cortex‐M3 中的特殊功能寄存器包括:
- 程序狀態寄存器組(PSRs 或曰 xPSR)
- 中斷屏蔽寄存器組(PRIMASK, FAULTMASK,以及 BASEPRI)
- 控制寄存器(CONTROL)
它們只能被專用的 MSR 和 MRS 指令訪問,而且它們也沒有存儲器地址。
- MRS <gp_reg>, <special_reg> ;讀特殊功能寄存器的值到通用寄存器
- MSR <special_reg>, <gp_reg> ;寫通用寄存器的值到特殊功能寄存器
程序狀態寄存器(PSRs 或曰 PSR)
程序狀態寄存器在其內部又被分爲三個子狀態寄存器:
- 應用程序 PSR(APSR)
- 中斷號 PSR(IPSR)
- 執行 PSR(EPSR)
通過 MRS/MSR 指令,這 3 個 PSRs 即可以單獨訪問,也可以組合訪問(2 個組合,3 個組合都可以)。當使用三合一的方式訪問時,應使用名字“xPSR”或者“PSR”。
stmdb sp!, {fp, ip, lr, pc} //sp=sp-4,sp=pc;先壓PC ,把內容壓入下一個內存地址即原地址-4.
//sp=sp-4,sp=lr;再壓lr
//sp=sp-4,sp=ip;再壓ip
//sp=sp-4,sp=fp;再壓fp
ldmia sp, {fp, sp, pc} //和stmdb成對使用,
//fp=sp,sp=sp+4;先彈fp //出棧還原+4
//sp=sp,sp=sp+4;先彈sp,此處的彈出不會 影響sp,因爲ldmia是一個機器週期執行完的。
//pc=sp,sp=sp+4;先彈pc
LDRH R0, [R13, #0xC] //加載無符號半字數據,即低16位
LDRB R0, [R13, #0x4] //加載一字節數據,即低8位
在特權級下,可以指定具體的堆棧指針,而不受當前使用堆棧的限制
MRS R0, MSP ; 讀取主堆棧指針到 R0
MSR MSP, R0 ; 寫入 R0 的值到主堆棧中
MRS R0, PSP ; 讀取進程堆棧指針到 R0
MSR PSP, R0 ; 寫入 R0 的值到進程堆棧中
通過讀取 PSP 的值,OS 就能夠獲取用戶應用程序使用的堆棧,進一步地就知道了在發
生異常時,被壓入寄存器的內容,而且還可以把其它寄存器進一步壓棧(使用STMDB和LDMIA
的書寫形式)。OS 還可以修改 PSP,用於實現多任務中的任務上下文切換。
堆棧指針 SP 指向最後一個被壓入堆棧的 32
位數值。在下一次壓棧時,SP 先自減 4,再存入新的數值。POP 操作剛好相反:先從 SP 指針處讀出上一次被壓入的值,再把 SP 指針自增 4。
常用加載指令
- MRS 加載特殊功能寄存器的值到通用寄存器
- MSR 存儲通用寄存器的值到特殊功能寄存器
- NOP 無操作
- SEV 發送事件
- WFE 休眠並且在發生事件時被喚醒
- WFI 休眠並且在發生中斷時被喚醒
- ISB 指令同步隔離(與流水線和 MPU 等有關——譯註) //隔離指令
- DSB 數據同步隔離(與流水線、MPU 和 cache 等有關——譯註) //隔離指令
- DMB 數據存儲隔離(與流水線、MPU 和 cache 等有關——譯註) //隔離指令
常用的多重存儲器訪問方式
LDMIA Rd!, {寄存器列表} 從 Rd 處讀取多個字。 每讀一個字後 Rd 自增一次,16
位寬度
STMIA Rd!, {寄存器列表} 存儲多個字到 Rd 處。 每存一個字後 Rd 自增一次,16
位寬度
LDMIA.W Rd!, {寄存器列表} 從 Rd 處讀取多個字。 每讀一個字後 Rd 自增一次,32
位寬度
LDMDB.W Rd!, {寄存器列表} 從 Rd 處讀取多個字。 每讀一個字前 Rd 自減一次,32
位寬度
STMIA.W Rd!, {寄存器列表} 存儲多個字到 Rd 處。 每存一個字後 Rd 自增一次,32
位寬度
STMDB.W Rd!, {寄存器列表} 存儲多個字到 Rd 處。 每存一個字前 Rd 自減一次,32
位寬度常用的存儲器訪問指令
示例 功能描述
LDRB Rd, [Rn, #offset] 從地址 Rn+offset 處讀取一個字節到 Rd
LDRH Rd, [Rn, #offset] 從地址 Rn+offset 處讀取一個半字到 Rd
LDR Rd, [Rn, #offset] 從地址 Rn+offset 處讀取一個字到 Rd
LDRD Rd1, Rd2, [Rn, #offset] 從地址 Rn+offset 處讀取一個雙字(64 位整數)到 Rd1(低32 位)和 Rd2(高 32 位)中。
STRB Rd, [Rn, #offset] 把 Rd 中的低字節存儲到地址 Rn+offset 處
STRH Rd, [Rn, #offset] 把 Rd 中的低半字存儲到地址 Rn+offset 處
STR Rd, [Rn, #offset] 把 Rd 中的低字存儲到地址 Rn+offset 處
LDRD Rd1, Rd2, [Rn, #offset] 把 Rd1(低 32 位)和 Rd2(高 32 位)表達的雙字存儲到地址 Rn+offset 處