【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)
作者 : 韓曙亮
博客地址 :?http://blog.csdn.net/shulianghan/article/details/42408137?
轉載請著名出處
本博客相關文檔下載 :?
-- ARM 匯編手冊 :?http://download.csdn.net/detail/han1202012/8328375
-- ARM 手冊 :?http://download.csdn.net/detail/han1202012/8324641
-- ARM 9 芯片文檔 :?http://download.csdn.net/detail/han1202012/8332389
-- ARM 11 芯片文檔 :?http://download.csdn.net/detail/han1202012/8332403
一. ARM 匯編概述
1. 匯編使用位置
匯編位置 :?
-- 啟動代碼 :?Bootloader 初始化時對 CPU 和 協處理器 等進行初始化, 此時沒有建立起 C 語言運行環境, 這個時候使用匯編語言執行初始化操作;
-- 效率要求 :?匯編效率高,?Linux 內核中, 對效率有特殊要求的地方需要匯編;
2. 匯編分類
(1) ARM 標準匯編
ARM 標準匯編簡介 :?
-- 使用場景 : 適用于ARM公司的匯編器, 適合在 Windows 平臺使用, 如ADS;
(2) GNU匯編
GNU 匯編簡介 :?
-- 使用場景 : 適用于 Linux 平臺交叉編譯工具鏈的匯編器;
3. ARM 匯編程序框架
ARM 匯編框架 :?
-- ARM 匯編框架示例 :?
.section .data< 初始化的數據> .section .bss< 未初始化的數據> .section .text .global _start _start:<匯編代碼>-- 程序入口 : "_start:" 是匯編程序的入口, 相當于 main();
-- 標注入口 : 使用 ".global _start" 標注程序入口, 外部才可以識別這是程序入口;
-- 標明代碼段 : ".section .text" 標明這是一個代碼段;
-- 標明 bss 段 : 使用 ".section .bss" 標明bss段, 如果沒有 bss 段 和 數據段, 直接從 .text 開始;
4. 搭建匯編開發調試環境
(1) 匯編程序準備
程序代碼 :?
-- 定義代碼段 : .text ;
-- 定義程序入口 : .globl _start;
-- 代碼示例 :?
.text .globl _start _start:mov r1,#1mov r2,#2mov r3,#3
Makefile 代碼 :
-- 鏈接 elf 格式文件 : 設置程序起始位置 6410板子是 0x50008000 地址;
-- 在 arm-linux-ld 指定程序起始地址 : 在 -Ttext 50008000 即可;
-- 如果使用鏈接器腳本指定地址 : 注意第三行指定程序起始地址;
SECTIONS {. = 0x50008000;. = ALIGN(4);.text :{led.o (.text)*(.text)}. = ALIGN(4);.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }. = ALIGN(4);.data : { *(.data) }. = ALIGN(4);.bss (NOLOAD) : { *(.bss) . = ALIGN(4); } }
-- 代碼示例 :?
all:start.oarm-linux-ld -Ttext 0x50008000 -o start.elf $^%.o:%.Sarm-linux-gcc -g -o $@ $^ -cclean:rm -rf *.o *.elf
(2) 啟動 JLink 調試
JLink 調試啟動 :?
-- 確保驅動安裝 : 注意 要安裝 Windows 驅動;
-- 連接 JLink : 虛擬機右下角連接 JLink;
-- 啟動?JLinkGDBServer :?
[root@localhost JLink_Linux_V434a]# ./JLinkGDBServer SEGGER J-Link GDB Server V4.34aJLinkARM.dll V4.34a (DLL compiled Aug 31 2011 11:51:40)Listening on TCP/IP port 2331J-Link connected Firmware: J-Link ARM V8 compiled Aug 24 2011 17:23:32 Hardware: V8.00 S/N: 17935099 Feature(s): RDI,FlashDL,FlashBP,JFlashJ-Link found 2 JTAG devices, Total IRLen = 5 JTAG ID: 0x07B76F0F (ARM11)
(2) eclipse 調試環境
搭建 eclipse 調試環境 :?
-- 導入工程 : 選擇 Makefile Project With Existing Code;
-- 選擇導入的代碼位置 :?
-- clean 代碼 : 選擇 "Project" --> "Clean";
-- build 工程 : 選擇 "菜單" --> Project --> Build All 選項即可;
-- 配置 Debug 調試參數 :?
-- 執行調試 : F6 單步調試走兩步, 可以再 Register 視圖中查看寄存器的值, 可以看到 r1 和 r2 被賦值為 1 和 2 了;
二. ARM 指令分類
?
ARM 匯編手冊 :?
-- CSDN 下載地址 : http://download.csdn.net/detail/han1202012/8328375.
轉載請著名出處
GNU 匯編 與 ARM 標準匯編區別 :?上面的手冊是 ARM 標準匯編手冊, 我們寫的是 GNU 匯編手冊, 有一定區別;
-- 大小寫區別 : ARM 標準匯編 都是大寫的, GNU 匯編可以是小寫字母;
1. 算術和邏輯指令
(1) MOV 指令
MOV 指令簡介 : 賦值操作;
-- 語法格式 : MOV <dest>, <op1>;
-- 語法解析 : dest 是目的寄存器, op1 可以是立即數, 也可以是寄存器, 地址等, 等價于 dest = op1;
匯編程序注釋 : 匯編中使用 "@" 符號添加注釋;
示例代碼 :?
.text .global _start _start:@mov 指令范例 mov r1, #8 @將 8 賦值給 r1 mov r2, r1 @將 r1 中的值賦值給 r2 mov r3, #10 @將 10 賦值給 r3 寄存器
(2) MVN 指令
MVN 指令簡介 : 取反賦值操作;
-- 語法格式 : MVN <dest>, <op1>;
-- 語法解析 : 將操作數 op1 取反后 賦值給 dest;
指令示例 :?
-- 代碼 :?
.text .global _start _start:@mvn 指令范例 mvn r1, #0b10 @0b10 二進制數取反, 賦值給 r1 mvn r2, #5 @5 十進制數取反, 賦值給 r2 mvn r3, r1 @將 r1 寄存器的值, 賦值給 r3
(3) SUB 指令
SUB 指令簡介?: 減法操作;
--?語法格式?: SUB <dest>, <op1>, <op2>;
--?語法解析?: dest 存放減法結果, op1 是減數, op2 是被減數, dest = op1 - op2;
-- 注意 : dest op1 都不能使用立即數, op2 可以使用立即數;
代碼示例 :?
.text .global _start _start:@sub 指令范例 @sub r1, #4, #2 錯誤示例, 減數不能是立即數, 必須是寄存器 mov r2, #4 sub r1, r2, #4 mov r0, #1 sub r3, r1, r0
(4) ADD 指令
ADD 指令簡介?: 加法操作;
--?語法格式?: ADD <dest>, <op1>, <op2>;
--?語法解析?: dest 存放加法結果, op1 和 op2 是相加的兩個數, dest = op1 + op2;
--?注意?:?dest op1 都不能使用立即數, op2 可以使用立即數;
代碼示例 :?
@add 指令范例 mov r2, #1 add r1, r2, #3(5) AND 指令
AND 指令簡介?: 邏輯與操作;
--?語法格式?: AND <dest>, <op1>, <op2>;
--?語法解析?: dest 存放邏輯與結果, op1 和 op2 是相與的兩個數, dest = op1 & op2;
--?注意?:?dest op1 都不能使用立即數, 必須使用寄存器, op2 可以使用立即數;
代碼示例 :?
.text .global _start _start:@and 指令范例 mov r1, #5 and r2, r1, #0mov r1, #5 mov r2, r1, #1(6) BIC 指令
BIC 指令簡介?: 位清除指令操作;
--?語法格式?: AND <dest>, <op1>, <op2>;
--?語法解析?: dest 存放位清除結果, op1 是被清除的對象, op2 是掩碼;
-- 示例 : "bic r0, r0, #0b1011", 清除 r0 中的 第0, 1, 3 位, 其余位保持不變, 結果放入 r0 中;
--?注意?:?dest op1 都不能使用立即數, 必須使用寄存器, op2 可以使用立即數;?
-- 二進制表示 : 掩碼中 % 在標準匯編中表示二進制, 但是在 GNU 匯編中無法使用, GNU 匯編中使用 0b 代表二進制;
代碼示例 :?
.text .global _start _start:@bic 指令范例 mov r1, #0b101011 bic r2, r1, #0b101 @將r1 的 0, 2 位清除
2. 比較指令
(1) CMP 指令
CMP 指令簡介?: 比較指令;
--?語法格式?: CMP <op1>, <op2>;
--?語法解析?: 比較結果有三種 op1 > op2 (CPSR N = 0), op1 = op2 (CPSR Z = 1), op1 < op2 (CPSR N = 1), 結果放入 CPSR 寄存器;
代碼示例 :?
.text .global _start _start:@cmp 指令范例 mov r1, #2 cmp r1, #1mov r1, #2 cmp r1, #3mov r1, #2 cmp r1, #2(2) TST 指令
TST 指令簡介?: 比較指令;
--?語法格式?: TST <op1>, <op2>;
--?語法解析?: op1 和 op2 按位與操作, 結果影響 CPSR 寄存器, 如果結果 不為 0, CPSR 的 Z = 0, 如果結果為0, Z = 1;
代碼示例?:?
.text .global _start _start:@cmp 指令范例 mov r1, #0b101 tst r1, #0b001 @按位與結果是 0b1, 結果不為0, CPSR Z = 0mov r1, #0b101 tst r1, #0b10 @按位與結果是 0, 結果不為3. 分支指令
(1) B 指令
B 指令簡介?: 分支指令;
--?語法格式?: B{條件} 地址;
--?語法解析?: 如果滿足條件, 就跳轉到 地址 位置, 如果不滿足條件, 就執行下面的語句, 如果沒有條件, 就是 100% 執行;;
代碼示例?:?
-- 條件分析 : gt 是大于條件, 如果 r1 > r2 就走條件分支, 否則就繼續執行下一條;
.text .global _start _start:@b 分支指令范例 mov r1, #6 mov r2, #5 cmp r1, r2 @比較 r1 和 r2 中的值 @b 后可以跟一個條件, {條件} 在 {} 中就是可加可不加, 如果沒有條件就是無條件100%執行 @gt 是大于條件指令, 如果條件滿足會跳轉到 branch1, 如果不滿足就執行下面的指令 bgt branch1 add r3, r1, r2 b end @這里為了不執行 branch1 操作, 直接跳轉到 end 執行branch1: sub r3, r1, r2end: nop(2) BL 指令
BL 指令簡介?: 帶連接的分支指令;
--?語法格式?: BL{條件} 地址;
--?語法解析?:?如果滿足條件, 就跳轉到 地址 位置,?如果不滿足條件, 就執行下面的語句,?如果沒有條件, 就是 100% 執行;;
代碼示例?:?
.text .global _start _start:@bl 帶連接的分支指令范例 mov r1, #2 cmp r1, #1 @此時跳轉到 func1, func1 執行完程序無法返回, 如果 使用 bl 跳轉, 程序會返回 @b func1 @此時使用 bl 跳轉到 func1 執行, func1 執行完畢后會返回執行下面的語句 bl func1 mov r1, #2 cmp r1, #3func1: mov r1, #2 cmp r1, #2mov r1, #4 cmp r1, #64. 移位指令
(1) LSL 指令
LSL 指令簡介?: 邏輯左移指令;
--?語法格式?: Rx, LSL#2;
--?語法解析?: 將 Rx 寄存器中的值, 左移2 位;
代碼示例?:
.text .global _start _start:@lsl 左移指令范例 mov r1, #0b1 @將 r1 中的值, 左移 2 位, 放入 r1 寄存器中 mov r1, r1, lsl#2(2) ROR 指令
ROR 指令簡介?: 循環右移指令;
--?語法格式?: Rx, ROR#2;
--?語法解析?: 將 Rx 寄存器中的值 循環右移 2 位;
代碼示例?:?
.text .global _start _start:@ror 循環右移指令范例 mov r1, #0b11 @結果是 ob1000...0001 mov r1, r1, ror#15. 程序狀態字訪問指令
程序狀態字 : CPSR 和 SPSR;
-- 注意 : 程序狀態字 不能使用 通用寄存器的語句 如 MOV 等訪問, 必須使用 程序狀態寄存器的 專用指令 讀寫;
轉載請著名出處
代碼示例?:?
.text .global _start _start:@mrs 指令范例 @rs 是 將 s -> r, sr 是 r -> s mrs r0, cpsr @將 cpsr 中的數據搬移到 r0 中 orr r0, #0b100 @將 cpsr 中的第三位置為1 msr cprs, r0
6. 存儲器訪問指令
(1) STR 指令
STR 指令簡介?: 將 寄存器中的值 保存到 內存中;
--?語法格式?: str r0, 地址;
--?語法解析?: 將 R0 寄存器中的值 保存到 內存地址中;;
代碼示例 :?
.text .global _start _start:@str 指令范例 mov r0, #0xff @將 r1 值改為 50000000 (OK-6410) str r0, [r1]-- 調試 : 添加地址監控, 在 Memory 視圖中進行監控;
(2) LDR 指令
LDR 指令簡介?:?將 寄存器中的值 保存到 內存中;
--?語法格式?: ldr r0, 地址;
--?語法解析?: 將 內存地址中 存放的值 加載入 r0 中;
代碼示例 :?
@ldr 指令范例 mov r0, #0xff @將 r1 值改為 50000000 (OK-6410) str r0, [r1] ldr r0, [r1]
7. 以上所有代碼示例
以上所有代碼示例 : 便于調試學習;
.text .global _start _start:@ldr 指令范例 mov r0, #0xff @將 r1 值改為 50000000 (OK-6410) str r0, [r1] ldr r0, [r1]@str 指令范例 mov r0, #0xff @將 r1 值改為 50000000 (OK-6410) str r0, [r1]@mrs msr 指令范例 @rs 是 將 s -> r, sr 是 r -> s mrs r0, cpsr @將 cpsr 中的數據搬移到 r0 中 orr r0, #0b100 程序入口, 用法 ".globol _start", 注意前面加上點;@將 cpsr 中的第三位置為1 msr cprs, r0@ror 循環右移指令范例 mov r1, #0b11 @結果是 ob1000...0001 mov r1, r1, ror#1@lsl 左移指令范例 mov r1, #0b1 @將 r1 中的值, 左移 2 位, 放入 r1 寄存器中 mov r1, r1, lsl#2@bl 帶連接的分支指令范例 mov r1, #2 cmp r1, #1 @此時跳轉到 func1, func1 執行完程序無法返回, 如果 使用 bl 跳轉, 程序會返回 @b func1 @此時使用 bl 跳轉到 func1 執行, func1 執行完畢后會返回執行下面的語句 bl func1 mov r1, #2 cmp r1, #3func1: mov r1, #2 cmp r1, #2mov r1, #4 cmp r1, #6@b 分支指令范例 mov r1, #6 mov r2, #5 cmp r1, r2 @比較 r1 和 r2 中的值 @b 后可以跟一個條件, {條件} 在 {} 中就是可加可不加, 如果沒有條件就是無條件100%執行 @gt 是大于條件指令, 如果條件滿足會跳轉到 branch1, 如果不滿足就執行下面的指令 bgt branch1 add r3, r1, r2 b end @這里為了不執行 branch1 操作, 直接跳轉到 end 執行branch1: sub r3, r1, r2end: nop@cmp 指令范例 mov r1, #0b101 tst r1, #0b001 @按位與結果是 0b1, 結果不為0, CPSR Z = 0mov r1, #0b101 tst r1, #0b10 @按位與結果是 0, 結果不為@cmp 指令范例 mov r1, #2 cmp r1, #1mov r1, #2 cmp r1, #3mov r1, #2 cmp r1, #2@bic 指令范例 mov r1, #0b101011 bic r2, r1, #0b101 @將r1 的 0, 2 位清除@and 指令范例 mov r1, #5 and r2, r1, #0mov r1, #5 mov r2, r1, #1@add 指令范例 mov r2, #1 add r1, r2, #3@mov 指令范例 mov r1, #8 @將 8 賦值給 r1 mov r2, r1 @將 r1 中的值賦值給 r2 mov r3, #10 @將 10 賦值給 r3 寄存器@mvn 指令范例 mvn r1, #0b10 @0b10 二進制數取反, 賦值給 r1 mvn r2, #5 @5 十進制數取反, 賦值給 r2 mvn r3, r1 @將 r1 寄存器的值, 賦值給 r3@sub 指令范例 @sub r1, #4, #2 錯誤示例, 減數不能是立即數, 必須是寄存器 mov r2, #4 sub r1, r2, #4 mov r0, #1 sub r3, r1, r0
三. ARM 偽指令
參考文檔 : ARM 文檔 Page 110, 上面有提供下載.
1. ARM 機器碼
(1) 機器碼反匯編示例
匯編程序執行流程 : 匯編代碼 --> 匯編器 --> 機器碼 --> CPU 運行;
反匯編示例?: 找到一個 elf 文件, 使用 arm-linux-objdump 反匯編;
-- 命令 : 使用?arm-linux-objdump -S -D start.elf 命令進行反匯編, 其中 "50008000: e3a01001 mov r1, #1 ; 0x1" 中的 "e3a01001" 就是機器碼, 如下圖標注部分;
-- 反匯編部分結果 :?
[root@localhost 04_assembly]# arm-linux-objdump -S -D start.elf start.elf: file format elf32-littlearmDisassembly of section .text:50008000 <_start>: .text .globl _start _start:mov r1,#1 50008000: e3a01001 mov r1, #1 ; 0x1mov r2,#2 50008004: e3a02002 mov r2, #2 ; 0x2mov r3,#3 50008008: e3a03003 mov r3, #3 ; 0x3 Disassembly of section .debug_aranges:
(2) 機器碼格式
機器碼格式 : 截圖自 arm 文檔 P110;
-- ARM 機器碼位數 : 32位;
-- 機器碼分段 :?
(3) 解析 MOV 指令機器碼
代碼準備 :?
-- 匯編代碼 :?
.text .globl _start _start:mov r0, r1moveq r0, #0xff
-- Makefile 腳本 :?
all:start.oarm-linux-ld -Ttext 0x50008000 -o start.elf $^%.o:%.Sarm-linux-gcc -g -o $@ $^ -cclean:rm -rf *.o *.elf
反匯編 elf 文件 :?
-- 反匯編內容 : 省略下面的大部分;
[root@localhost 04_assembly]# arm-linux-objdump -S -D start.elf start.elf: file format elf32-littlearmDisassembly of section .text:50008000 <_start>: .text .globl _start _start:mov r0, r1 50008000: e1a00001 mov r0, r1moveq r0, #0xff 50008004: 03a000ff moveq r0, #255 ; 0xff Disassembly of section .debug_aranges:
匯編對應機器碼 :?
-- "mov r0, r1" : 十六進制 0xe1a00001, 二進制 ? ? ?11100001101000000000000000000001;
-- "moveq r0, #0xff" : 十六進制 0x03a000ff, 二進制 00000011101000000000000011111111;
機器碼解析 :?
第一條 : 1110 00 0 1101 0 0000 0000 000000000001
第二條 : 0000 00?1 1101 0 0000 0000 000011111111
-- 條件位對比 (第一段 31 ~ 28)?: ?第一條是 1110 對應 AL 總是執行, ?第二條是 0000 對應 EQ;
-- 保留位對比 (第二段 27 ~ 26) : 第一條 00, 第二條 00, 明顯都一樣;
-- I 操作數類型標識位 (第三段 25)?: 標志最后一個存立即數 還是寄存器, 如果是 0 表示寄存器, 如果是 1 表示立即數;
-- 操作碼位 (第四段 24 ~ 21) : 區分不同指令, 1101 是 MOV 指令;
-- S 狀態寄存器改變標識 (第五段 20) : 是否影響 CPSR 寄存器, 如果 S = 0 不影響, 如果 S = 1 影響;
-- Rn 源操作寄存器 (第六段 19 ~ 16) : MOV 和 MVN 不使用 Rn 位, 寄存器編號;
-- Rd 目的操作寄存器 (第七段 15 ~ 12) : 寄存器編號;
-- shifter_operand 源操作書 (第八段 11 ~ 0)?: 源操作數, 這個與 I 位結合起來, 如果 I = 0, 該位表示寄存器編號, 如果 I = 1, 該位表示 立即數大小, 立即數是有范圍的, 如果超出會報錯, 這里就需要使用偽指令了;
(4) 機器碼相關文檔
相關文檔 :?
-- 位數文檔?: P116, The ARM Instruction Set, A3.4.1 Instruction encoding;
-- MOV 和 MVN 指令 : 機器碼格式 "<opcode1>{<cond>}{S} <Rd>, <shifter_operand>", 沒有 Rn 字段, 該字段沒用;
轉載請著名出處
-- 條件位文檔 : Page 112, The ARM Instruction Set, A3.2.1 Condition code 0b1111;
2. 偽指令
偽指令簡介?: 偽指令沒有對應的機器碼, 這種指令只在編譯時起作用, 偽指令需要轉化成 其它匯編指令運行, 如 定義 宏, 不會產生機器碼;
(1) globol 偽指令
globol 偽指令介紹 :?
-- 偽指令作用 : 用于定義 程序入口, 用法 ".globol _start", 注意前面加上點;
-- 代碼示例 :?
(2) data acsii byte word 偽指令
偽指令介紹?:?
--?偽指令作用?: data 用于定義 數據段, 標明后面的數據存放到數據段中; acsii 標明字符串變量類型, byte 標明 byte 類型變量, word ?標明 word 類型變量;
代碼示例 :?
-- 匯編代碼?: start.S ;
.data @定義數據變量 hello: @標明變量地址, 字符串變量 .ascii "Hello World !" bh: @標明變量地址, byte 變量 .byte 0x1 ADD: @標明變量地址, word 變量 .word 0xff.text .global _start _start:mov r0, #0xff -- make 腳本 : Makefile;
all: start.o arm-linux-ld -Ttext 0x50008000 -o start.elf start.ostart.o : start.Sarm-linux-gcc -g -o start.o -c start.S.PHONY: clean clean:rm *.o *.elf *.bin
分析 elf 文件 : 使用 arm-linux-readelf -a start.elf 命令分析 start.elf 文件;?
-- .data 段地址 : 注意 [2] 中 .data 地址為 0x50010004;
-- 數據變量 :?
-- elf 文件分析全文 :?
octopus@octopus:~/arm/demo$ arm-linux-readelf -a start.elf ELF Header:Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: EXEC (Executable file)Machine: ARMVersion: 0x1Entry point address: 0x50008000Start of program headers: 52 (bytes into file)Start of section headers: 33100 (bytes into file)Flags: 0x5000002, has entry point, Version5 EABISize of this header: 52 (bytes)Size of program headers: 32 (bytes)Number of program headers: 2Size of section headers: 40 (bytes)Number of section headers: 11Section header string table index: 8Section Headers:[Nr] Name Type Addr Off Size ES Flg Lk Inf Al[ 0] NULL 00000000 000000 000000 00 0 0 0[ 1] .text PROGBITS 50008000 008000 000004 00 AX 0 0 4[ 2] .data PROGBITS 50010004 008004 000012 00 WA 0 0 1[ 3] .debug_aranges PROGBITS 00000000 008018 000020 00 0 0 8[ 4] .debug_info PROGBITS 00000000 008038 000048 00 0 0 1[ 5] .debug_abbrev PROGBITS 00000000 008080 000014 00 0 0 1[ 6] .debug_line PROGBITS 00000000 008094 000037 00 0 0 1[ 7] .ARM.attributes ARM_ATTRIBUTES 00000000 0080cb 000014 00 0 0 1[ 8] .shstrtab STRTAB 00000000 0080df 00006c 00 0 0 1[ 9] .symtab SYMTAB 00000000 008304 000180 10 10 13 4[10] .strtab STRTAB 00000000 008484 000087 00 0 0 1 Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings)I (info), L (link order), G (group), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)There are no section groups in this file.Program Headers:Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg AlignLOAD 0x008000 0x50008000 0x50008000 0x00004 0x00004 R E 0x8000LOAD 0x008004 0x50010004 0x50010004 0x00012 0x00012 RW 0x8000Section to Segment mapping:Segment Sections...00 .text 01 .data There is no dynamic section in this file.There are no relocations in this file.There are no unwind sections in this file.Symbol table '.symtab' contains 24 entries:Num: Value Size Type Bind Vis Ndx Name0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 50008000 0 SECTION LOCAL DEFAULT 1 2: 50010004 0 SECTION LOCAL DEFAULT 2 3: 00000000 0 SECTION LOCAL DEFAULT 3 4: 00000000 0 SECTION LOCAL DEFAULT 4 5: 00000000 0 SECTION LOCAL DEFAULT 5 6: 00000000 0 SECTION LOCAL DEFAULT 6 7: 00000000 0 SECTION LOCAL DEFAULT 7 8: 50010004 0 NOTYPE LOCAL DEFAULT 2 hello9: 50010011 0 NOTYPE LOCAL DEFAULT 2 bh10: 50010011 0 NOTYPE LOCAL DEFAULT 2 $d11: 50010012 0 NOTYPE LOCAL DEFAULT 2 ADD12: 50008000 0 NOTYPE LOCAL DEFAULT 1 $a13: 50008004 0 NOTYPE GLOBAL DEFAULT ABS __exidx_end14: 50010016 0 NOTYPE GLOBAL DEFAULT ABS _bss_end__15: 50010016 0 NOTYPE GLOBAL DEFAULT ABS __bss_start__16: 50008004 0 NOTYPE GLOBAL DEFAULT ABS __exidx_start17: 50010016 0 NOTYPE GLOBAL DEFAULT ABS __bss_end__18: 50008000 0 NOTYPE GLOBAL DEFAULT 1 _start19: 50010016 0 NOTYPE GLOBAL DEFAULT ABS __bss_start20: 50010018 0 NOTYPE GLOBAL DEFAULT ABS __end__21: 50010016 0 NOTYPE GLOBAL DEFAULT ABS _edata22: 50010018 0 NOTYPE GLOBAL DEFAULT ABS _end23: 50010004 0 NOTYPE GLOBAL DEFAULT 2 __data_startNo version information found in this file. Attribute Section: aeabi File AttributesTag_CPU_arch: v4Tag_ARM_ISA_use: Yes
(3) equ 偽指令
equ 偽指令介紹?:?
--?偽指令作用?: 該指定作用是定義常量;
--?代碼示例?:?
.text .global _start _start:@定義一個宏變量 .equ DA, 0x68@將 DA 值賦值給 r0 寄存器 mov r0, #DA
(4) align 偽指令
align 偽指令介紹?:?
--?偽指令作用?: 標明數據對齊;
對齊代碼示例 :?
-- 含有對齊的代碼 :?
.data @定義數據變量 hello: @標明變量地址, 字符串變量 .ascii "Hello World !" bh: @標明變量地址, byte 變量 .byte 0x1 ADD: @標明變量地址, word 變量 .word 0xff.text .global _start _start:@定義一個宏變量 .equ DA, 0x68@將 DA 值賦值給 r0 寄存器 mov r0, #DA-- 不含對齊的代碼 :?
.data @定義數據變量 hello: @標明變量地址, 字符串變量 .ascii "Hello World !" .align 4 bh: @標明變量地址, byte 變量 .byte 0x1 ADD: @標明變量地址, word 變量 .word 0xff.text .global _start _start:@定義一個宏變量 .equ DA, 0x68@將 DA 值賦值給 r0 寄存器 mov r0, #DA代碼 elf 內容對比 : 這里省略大部分, 只給出內存對應地址, 查看對齊內容;
-- 沒有對齊的代碼 : 0x50010011 明顯不能被 4 整除;-- 對齊的代碼 : 0x50010020 可以被4整除, 此時已經進行了對齊;
3. 操作類偽指令
(1) ldr 偽指令
機器碼 shifter_operand 段解析 :?
-- 段解析 : 其中 4 位存放位移值, 8 位存放數值, 因此 立即數不能超過 8位, 最大 0xFF;
-- 缺陷 : 無法使用 大的數字;
-- 示例 :?
.text .global _start _start:mov r0, #0xFFF-- 編譯錯誤 :?
octopus@octopus:~/arm/demo$ make arm-linux-gcc -g -o start.o -c start.S start.S: Assembler messages: start.S:5: Error: invalid constant (fff) after fixup make: *** [start.o] 錯誤 1
ldr 偽指令 :?
-- 作用 : 可以 向寄存器中賦值 大立即數;
-- 語法格式 : "ldr r0, =0xFFF", 注意 不使用 # , 使用 = 后面加上立即數;
-- 代碼示例 : 此時能編譯成功, 0xfff 被賦值給 r0 寄存器;
.text .global _start _start:ldr r0, =0xFFF-- 反匯編 elf 代碼 :?
octopus@octopus:~/arm/demo$ arm-linux-objdump -S -D start.elf start.elf: file format elf32-littlearmDisassembly of section .text:50008000 <_start>: .text .global _start _start:ldr r0, =0xFFF 50008000: e51f0004 ldr r0, [pc, #-4] ; 50008004 <_start+0x4> 50008004: 00000fff .word 0x00000fff Disassembly of section .debug_aranges:... ...-- 分析反匯編代碼 : "50008000: e51f0004 ldr r0, [pc, #-4] ; 50008004 <_start+0x4>" 代碼表明?ldr r0, =0xFFF 是使用 ldr 讀取內存指令, 從 pc - 4 地址上讀取該地址存儲的值, "50008004: 00000fff .word 0x00000fff" ?表明 系統將 0xFFF 定義在了 pc -4 內存地址中;
(2) nop 偽指令
nop 偽指令?:?
--?作用?: 進行延時, 在一些對時序要求較高的程序中, 使用該指令進行一個時鐘的延時;
--?代碼示例?:?
.text .global _start _start:nop-- 反匯編 : nop 偽指令執行了 "mov r0, r0" 這個無意義的操作;
octopus@octopus:~/arm/demo$ arm-linux-objdump -S -D start.elf start.elf: file format elf32-littlearmDisassembly of section .text:50008000 <_start>: .text .global _start _start:nop 50008000: e1a00000 nop (mov r0,r0) Disassembly of section .debug_aranges:... ...
三. 協處理器訪問指令
1. 協處理器簡介
協處理器簡介 :?
-- 作用 : 執行特定處理任務, 減輕處理器負擔;
-- 數學協處理器 : 主要進行數字處理;
-- 協處理器支持 : ARM 芯片最多支持 16 個協處理器, 最重要的協處理器 是 CP15;
CP15 協處理器作用 : CP15 是系統控制寄存器, 通過這些寄存器, 配置與控制 緩存, MMU, 保護系統, 時鐘模式 和 其它系統參數;
-- 如何訪問 CP15 : 通過訪問 CP15 中的寄存器控制上面的參數, CP15 提供了 16 組寄存器;
-- 文檔 :?
?
2. 協處理器訪問指令
mcr 指令解析 : 詳情見 ARM11 文檔, P145, 3.2;
-- 作用 : 將本地寄存器中的數據 賦值給 CP15 的寄存器;
-- 語法格式 : "MCR{cond} P15,<Opcode_1>,<Rd>,<CRn>,<CRm>,<Opcode_2>";
-- 語法解析 : CRn 表示 CP15 寄存器屬于哪一組, CRm 也是組名;
-- 代碼示例 :?
.text .global _start _start:@"MCR{cond} P15,<Opcode_1>,<Rd>,<CRn>,<CRm>,<Opcode_2>" @讀取 MainID 寄存器 mcr p15, 0, r0, c0, c0, 0
-- 文檔截圖 :?
-- CP15 寄存器訪問 : 如果讀取 MainID 寄存器, 就取前面的哪些 CRn Op1 CRm Op2 等參數;
作者?:?韓曙亮
博客地址?:?http://blog.csdn.net/shulianghan/article/details/42408137?
轉載請著名出處
本博客相關文檔下載?:?
--?ARM 匯編手冊?:?http://download.csdn.net/detail/han1202012/8328375
--?ARM 手冊?:?http://download.csdn.net/detail/han1202012/8324641
--?ARM 9 芯片文檔?:?http://download.csdn.net/detail/han1202012/8332389
--?ARM 11 芯片文檔?:?http://download.csdn.net/detail/han1202012/8332403
總結
以上是生活随笔為你收集整理的【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【嵌入式开发】ARM 芯片简介 (ARM
- 下一篇: 【嵌入式开发】 Bootloader 详