一. 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,#1 mov r2,#2 mov 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.o arm-linux-ld -Ttext 0x50008000 -o start.elf $^ %.o:%.S arm-linux-gcc -g -o $@ $^ -c clean: rm -rf *.o *.elf
(2) 啟動 JLink 調試
JLink 調試啟動 :
-- 確保驅動安裝 : 注意 要安裝 Windows 驅動;
-- 連接 JLink : 虛擬機右下角連接 JLink;
-- 啟動 JLinkGDBServer :
[root@localhost JLink_Linux_V434a]# ./JLinkGDBServer SEGGER J-Link GDB Server V4.34a JLinkARM.dll V4.34a (DLL compiled Aug 31 2011 11:51:40) Listening on TCP/IP port 2331 J-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,JFlash J-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 匯編手冊 :
GNU 匯編 與 ARM 標準匯編區別 : 上面的手冊是 ARM 標準匯編手冊, 我們寫的是 GNU 匯編手冊, 有一定區別;
-- 大小寫區別 : ARM 標準匯編 都是大寫的, GNU 匯編可以是小寫字母;
1. 算術和邏輯指令
(1) MOV 指令
MOV 指令簡介 : 賦值操作;
-- 語法格式 : MOV -- 語法解析 : dest 是目的寄存器, op1 可以是立即數, 也可以是寄存器, 地址等, 等價于 dest = op1; 匯編程序注釋 : 匯編中使用 '@' 符號添加注釋; 示例代碼 : (2) MVN 指令 MVN 指令簡介 : 取反賦值操作; -- 語法格式 : MVN -- 語法解析 : 將操作數 op1 取反后 賦值給 dest; 指令示例 : -- 代碼 : (3) SUB 指令 SUB 指令簡介 : 減法操作; -- 語法格式 : SUB -- 語法解析 : dest 存放減法結果, op1 是減數, op2 是被減數, dest = op1 - op2; -- 注意 : dest op1 都不能使用立即數, op2 可以使用立即數; 代碼示例 : (4) ADD 指令 ADD 指令簡介 : 加法操作; -- 語法格式 : ADD -- 語法解析 : dest 存放加法結果, op1 和 op2 是相加的兩個數, dest = op1 + op2; -- 注意 : dest op1 都不能使用立即數, op2 可以使用立即數; 代碼示例 : AND 指令簡介 : 邏輯與操作; -- 語法格式 : AND -- 語法解析 : dest 存放邏輯與結果, op1 和 op2 是相與的兩個數, dest = op1 & op2; -- 注意 : dest op1 都不能使用立即數, 必須使用寄存器, op2 可以使用立即數; 代碼示例 : BIC 指令簡介 : 位清除指令操作; -- 語法格式 : AND -- 語法解析 : dest 存放位清除結果, op1 是被清除的對象, op2 是掩碼; -- 示例 : 'bic r0, r0, #0b1011', 清除 r0 中的 第0, 1, 3 位, 其余位保持不變, 結果放入 r0 中; -- 注意 : dest op1 都不能使用立即數, 必須使用寄存器, op2 可以使用立即數; -- 二進制表示 : 掩碼中 % 在標準匯編中表示二進制, 但是在 GNU 匯編中無法使用, GNU 匯編中使用 0b 代表二進制; 代碼示例 : 2. 比較指令 (1) CMP 指令 CMP 指令簡介 : 比較指令; -- 語法格式 : CMP -- 語法解析 : 比較結果有三種 op1 > op2 (CPSR N = 0), op1 = op2 (CPSR Z = 1), op1 < op2 (CPSR N = 1), 結果放入 CPSR 寄存器; 代碼示例 : (2) TST 指令 TST 指令簡介 : 比較指令; -- 語法格式 : TST -- 語法解析 : op1 和 op2 按位與操作, 結果影響 CPSR 寄存器, 如果結果 不為 0, CPSR 的 Z = 0, 如果結果為0, Z = 1; 代碼示例 : 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, r2 end: nop 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, #3 func1: mov r1, #2 cmp r1, #2 mov r1, #4 cmp r1, #6 4. 移位指令 (1) LSL 指令 LSL 指令簡介 : 邏輯左移指令; -- 語法格式 : Rx, LSL#2; -- 語法解析 : 將 Rx 寄存器中的值, 左移2 位; 代碼示例 : (2) ROR 指令 ROR 指令簡介 : 循環右移指令; -- 語法格式 : Rx, ROR#2; -- 語法解析 : 將 Rx 寄存器中的值 循環右移 2 位; 代碼示例 : 程序狀態字 : CPSR 和 SPSR; -- 注意 : 程序狀態字 不能使用 通用寄存器的語句 如 MOV 等訪問, 必須使用 程序狀態寄存器的 專用指令 讀寫; 代碼示例 : 6. 存儲器訪問指令 (1) STR 指令 STR 指令簡介 : 將 寄存器中的值 保存到 內存中; -- 語法格式 : str r0, 地址; -- 語法解析 : 將 R0 寄存器中的值 保存到 內存地址中;; 代碼示例 : -- 調試 : 添加地址監控, 在 Memory 視圖中進行監控; (2) LDR 指令 LDR 指令簡介 : 將 寄存器中的值 保存到 內存中; -- 語法格式 : ldr r0, 地址; -- 語法解析 : 將 內存地址中 存放的值 加載入 r0 中; 代碼示例 : 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, #3 func1: mov r1, #2 cmp r1, #2 mov 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, r2 end: nop @cmp 指令范例 .text
.global _start
_start:
@mov 指令范例
mov r1, #8 @將 8 賦值給 r1
mov r2, r1 @將 r1 中的值賦值給 r2
mov r3, #10 @將 10 賦值給 r3 寄存器
.text
.global _start
_start:
@mvn 指令范例
mvn r1, #0b10 @0b10 二進制數取反, 賦值給 r1
mvn r2, #5 @5 十進制數取反, 賦值給 r2
mvn r3, r1 @將 r1 寄存器的值, 賦值給 r3
.text
.global _start
_start:
@sub 指令范例
@sub r1, #4, #2 錯誤示例, 減數不能是立即數, 必須是寄存器
mov r2, #4
sub r1, r2, #4
mov r0, #1
sub r3, r1, r0
@add 指令范例
mov r2, #1
add r1, r2, #3
(5) AND 指令.text
.global _start
_start:
@and 指令范例
mov r1, #5
and r2, r1, #0
mov r1, #5
mov r2, r1, #1
(6) BIC 指令.text
.global _start
_start:
@bic 指令范例
mov r1, #0b101011
bic r2, r1, #0b101 @將r1 的 0, 2 位清除
.text
.global _start
_start:
@cmp 指令范例
mov r1, #2
cmp r1, #1
mov r1, #2
cmp r1, #3
mov r1, #2
cmp r1, #2
.text
.global _start
_start:
@cmp 指令范例
mov r1, #0b101
tst r1, #0b001 @按位與結果是 0b1, 結果不為0, CPSR Z = 0
mov r1, #0b101
tst r1, #0b10 @按位與結果是 0, 結果不為
(2) BL 指令.text
.global _start
_start:
@lsl 左移指令范例
mov r1, #0b1
@將 r1 中的值, 左移 2 位, 放入 r1 寄存器中
mov r1, r1, lsl#2
.text
.global _start
_start:
@ror 循環右移指令范例
mov r1, #0b11
@結果是 ob1000...0001
mov r1, r1, ror#1
5. 程序狀態字訪問指令.text
.global _start
_start:
@mrs 指令范例
@rs 是 將 s -> r, sr 是 r -> s
mrs r0, cpsr @將 cpsr 中的數據搬移到 r0 中
orr r0, #0b100 @將 cpsr 中的第三位置為1
msr cprs, r0
.text
.global _start
_start:
@str 指令范例
mov r0, #0xff
@將 r1 值改為 50000000 (OK-6410)
str r0, [r1]
@ldr 指令范例
mov r0, #0xff
@將 r1 值改為 50000000 (OK-6410)
str r0, [r1]
ldr r0, [r1]
上一篇:基于WINCE6.0+S3C2443的camera驅動
下一篇:【嵌入式開發】ARM 芯片簡介 (ARM芯片類型 | ARM處理器工作模式 | ARM 寄存器 | ARM 尋址)
推薦閱讀最新更新時間:2025-05-22 17:59

設計資源 培訓 開發板 精華推薦
- 青柚工作室PCB尺_NFC版
- AVR602,使用 ATtinyX3U 的應用電路
- RS232 to USB
- NCP1094GEVB:PoE-PD 接口控制器評估板
- 使用 Analog Devices 的 LTC1439IG 的參考設計
- AD8188-EVALZ,用于 350 MHz 單電源三路 2:1 多路復用器的評估板
- 基于A6986H的38 V,2 A同步降壓型開關穩壓器評估板
- AD9265-125EBZ 16 位 125 Msps 評估板(兼容 HSC-ADC-EVALCZ)
- LTC2946CMS-1 雙電源、電荷和能量監視器的典型應用,使用單個光耦合器進行電流隔離,并在任一電源出現故障時使用阻塞二極管來保持數據
- 基于ESP32-WROOM-32D(16MB)的開發板設計