ADS下arm匯編

ARM 270瀏覽
內容:

ARM指令集介紹
ARM指令教程
ARM常用指令,偽指令
ARM C與匯編混和編程
ARM匯編樣例: 使用匯編程序來控制LED

編寫一些基本匯編語言程序,用匯編實驗一個LED 燈的亮和熄滅. 掌握ARM 匯編語言編程,掌握ARM 匯編語言和C混和編程
ARM指令集介紹
?
ARM CPU是RISC體系結構,相對于X86的CISC體系,指令集大大簡化.因此ARM匯編一般也比X86的匯編比較易學.
ARM的偽指令
ARM的偽指令是類似于C語言的宏指令,本身不是CPU指令集的一部分,只是為了簡化程序編寫而設計的,在編譯前也必須將其預處理掉.因為不是CPU標準指令集.所以不同編譯器可能采用不同偽指令集.ARM匯編編程目前主要有兩個類偽指令集.一種是GNU GAS采用的AT&T格式,象大部分實用的BOOTLOADER(vivi,uboot)和Linux內核的匯編代碼均采用這種格式.
清單1. 一個使用退出碼 2退出的程序
行號 GAS
?
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
# Text segment begins
.section .text
?
?? .globl _start
?
# Program entry point
?? _start:
?
# Put the code number for system call
????? movl?$1, %eax
?
/* Return value */
????? movl?$2, %ebx
?
# Call the OS
????? int?? $0x80
?
? ? ? ?
有比較明顯的特征,#和 /* */作為注釋,偽指令一用小寫,并以.打頭.
一類是ADS的ARM 編譯器采用的另外一種風格.偽指令用大寫,不用. 注釋采用@和;打頭,寄存器名字前面也不用%打頭
?
行號
ARM 匯編
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
?
COUNT????????????? EQU????????? 0x40003100???? ;
定義一個變量,地址為0x40003100???????
???????????????????????????
??????????????????????????? AREA??????? Example2,CODE,READONLY?? ;
聲明代碼段Example2
??????????????????????????? ENTRY???????????????????????????????? ;
標識程序入口
??????????????????????????? CODE32????????????????????????????? ;
聲明32ARM指令
START??????????????? LDR????????? R1,=COUNT???? ; R1 <= COUNT
??????????????????????????? MOV????????? R0,#0???????????????? ; R0 <= 0
??????????????????????????? STR?????????? R0,[R1]????????????? ; [R1] <= R0,即設置COUNT0
???????????????????????????????????????????????????????
LOOP?? ?????????? LDR????????? R1,=COUNT????
??????????????????????????? LDR????????? R0,[R1]????????????? ; R0 <= [R1]
??????????????????????????? ADD????????? R0,R0,#1 ; R0 <= R0 + 1
??????????????????????????? CMP????????? R0,#10?????????????? ; R010比較,影響條件碼標志
??????????????????????????? MOVHS???? R0,#0???????????????? ;
R0大于等于10,則此指令執行,R0 <= 0
??????????????????????????? STR?????????? R0,[R1]????????????? ; [R1] <= R0,即保存COUNT
???????????????????????????
??????????????????????????? B??????????????? LOOP
???????????????????????????
??????????????????????????? END
?
?
在ADS下,一般匯編程序后綴名被約定為 .s(匯編源代碼)或.inc(匯編包含文件)
ARM程序的執行
1.在模擬器執行匯編
如果用開發一般在匯編,或C語言程序,只要不牽涉到外部設備使用.可以用AXD模擬器來調試.而無需真實的硬件
源代碼
Hello.s的項目配置
l如果在真實的硬件平臺測試,需要調整image RO Base 0x40000000(代碼裝入區,) RW BASE 0x40003000(可寫區首址)
l主要缺省的0x8000并不是一個S3C2410合法的地址,強行在這個地址運行會觸發”Data Abort”異常.
為此調整到0x4000000,這一段合法的RAM地址
配置Image的執行代碼入口=0x40000000

?
ARM ADS程序分區映象
lGCC編譯程序分區
–Gcc 的編譯程序文件成功后,最后都會生一個.out或ELF格式的可執行文件,這個文件通常都包含三個段.text,.data和.bss段,
–運行時,會在進程空間會生成.text,.data.bss和stack,heap五個區.
l在ADS下,可執行文件有兩種,一種是.axf文件,帶有調試信息的ELF可執行文件,可供AXD調試工具使用.另一種是.bin
l跟GCC程序對應,ADS編寫的程序也有兩種狀態,一個保存狀態.對于ELF可執行文件,一種是運行態,對應進程空間分區.
–在保存狀態時,分別是代碼段和數據段。代碼段又分為可執行代碼段(.text)和只讀數據段(.rodata), 數據段又分為初始化數據段(.data)和未初始化數據段(.bss)。
–可執行文件通過裝載過程, 搬入到RAM中運行, 這時候可執行文件就變成運行態。這時各區的名字變成了RO,RW和ZI段.
l 只讀的代碼段和常量被稱作RO段(ReadOnly); 對應.text和.rodata
l 可讀寫的全局變量和靜態變量被稱作RW段(ReadWrite) ,對應.data和.bss
l RW段中要被初始化為零的變量被稱為ZI段(ZeroInit)。 ,對應.bss段
l對于嵌入式系統而言,程序映象都是存儲在Flash存儲器等一些非易失性器件中的,而在運行時,程序中的RW段必須重新裝載到可讀寫的RAM中簡單來說,程序的加載時域就是指程序燒入Flash中的狀態,運行時域是指程序執行時的狀態。
–對于比較簡單的情況,可以在ADS集成開發環境的ARM?LINKER選項中指定RO?BASE和RW?BASE,告知連接器RO和RW的連接基地址。
l在前一例即是采用這一方法
l因為RO段包含數據和代碼,所以RO BASE不一定等于可執行程序的入口.所以在ADS里通常還要手工指定 Image Entry Point,它即可以等于或大于RO BASE的地址.
–對于復雜情況,如RO段被分成幾部分并映射到存儲空間的多個地方時,需要創建一個稱為“分布裝載描述文件”的文本文件,通知連接器把程序的某一部分連接在存儲器的某個地址空間。
ADS成功編譯后結果
在ADS的C和匯編里,可以用如下保留變量來取出各個值
lImage$$RO$$Base?RO基地址
lImage$$RO$$Limit RO最大地址
lImage$$RW$$Base RW段的基地址
lImage$$RW$$Limit RW段的最大地址
lImage$$ZI$$Base ZI段的基地址
lImage$$ZI$$Limit ZI段的最大地址
?
在編譯后,ARM編譯器會把上述保留字由真實的地址代替,以下是一個實例。可以看出ZI是RW一部分

ARM C與匯編混和編程
在嵌入式系統開發中,目前使用的主要編程語言是C和匯編,C++已經有相應的編譯器,但是現在使用還是比較少的。在稍大規模的嵌入式軟件中,示例含有OS,大部分的代碼都是用C 編寫的,主要是因為C 語言的結構比較好,便于人的理解,而且有大量的支持庫。盡管如此,很多地方還是要用到匯編語言,示例bootloader和操作系統的硬件系統的初始化,包括CPU 狀態的設定,中斷的使能,主頻的設定,以及RAM的控制參數及初始化,一些中斷處理方面也可能涉及匯編。另外一個使用匯編的地方就是一些對性能非常敏感的代碼塊,這是不能依靠C編譯器的生成代碼,而要手工編寫匯編,達到優化的目的。而且,匯編語言是和CPU
的指令集緊密相連的,作為涉及底層的嵌入式系統開發,熟練對應.
?
匯編語言與C/C++的混合編程通常有以下幾種方式:
??????? - 在C/C++代碼中嵌入匯編指令
??????? - 在匯編程序和C/C++的程序之間進行變量的互訪
??????? - 匯編程序、C/C++程序間的相互調用
?
1.在C 語言中內嵌匯編
?在C 中內嵌的匯編指令包含大部分的ARM 和Thumb 指令,不過其使用與匯編文件中的指令有些不同,存在一些限制,主要有下面幾個方面:
1)不能直接向PC寄存器賦值,程序跳轉要使用B或者BL指令
2)在使用物理寄存器時,不要使用過于復雜的C 表達式,避免物理寄存器沖突
3)R12和R13 可能被編譯器用來存放中間編譯結果,計算表達式值時可能將R0 到R3、R12及R14用于子程序調用,因此要避免直接使用這些物理寄存器
4)一般不要直接指定物理寄存器,而讓編譯器進行分配
內嵌匯編使用的標記是 _asm或者asm關鍵字,,但GNU 中,采用 __asm(“ ”);格式
ADS
C程序內嵌匯編

#include <stdio.h>
void my_strcpy(const char *src, char *dest)
{
? char ch;
? _asm
? {
??? loop:
??? ldrb ch, [src], #1
??? strb ch, [dest], #1
??? cmp ch, #0
??? bne loop
? }

}
int main()
{
? char *a = "forget it and move on!";
? char b[64];
? my_strcpy(a, b);
? printf("original: %s", a);
? printf("copyed: %s", b);
? return 0;
}

?
在上例中, 在這里C 和匯編之間的值傳遞是用C 的指針來實現的,因為指針對應的是地址,所以匯編中也可以訪問。
2.在匯編,C程序之間的全局變量的傳遞
內嵌匯編不用單獨編輯匯編語言文件,比較簡潔,但是有諸多限制,當匯編的代碼較多時一般放在單獨的匯編文件中。這時就需要在匯編和C 之間進行一些數據的傳遞,最簡便的辦法就是使用全局變量。
ADS
帶有全局變量的C程序

/* cfile.c
*
定義全局變量,并作為主調程序
*/
#include <stdio.h>
int gVar_1 = 12;
extern asmDouble(void);
int main()
{
? printf("original value of gVar_1 is: %d", gVar_1);
? asmDouble();//
使用匯編代碼段
? printf(" modified value of gVar_1 is: %d", gVar_1);
? return 0;

?
ADS
使用全局變量的匯編程序

;called by main(in C),to double an integer, a global var defined in C is used.
AREA asmfile, CODE, READONLY
EXPORT asmDouble

IMPORT gVar_1
asmDouble //匯編子函數asmDouble
ldr r0, =gVar_1
ldr r1, [r0]
mov r2, #2
mul r3, r1, r2
str r3, [r0]
mov pc, lr
END

?
3.在C 中調用匯編的函數
在C 中調用匯編文件中的函數,要做的主要工作有兩個,一是在C 中聲明函數原型,并加extern關鍵字;二是在匯編中用EXPORT 導出函數名,并用該函數名作為匯編代碼段的標識,最后用mov pc, lr返回。然后,就可以在C 中使用該函數了。從C的角度,并不知道該函數的實現是用C還是匯編。更深的原因是因為C 的函數名起到表明函數代碼起始地址的左右,這個和匯編的label是一致的。
ADS
C中調用匯編函數

/* cfile.c
* in C,call an asm function, asm_strcpy
* Sep 9, 2004
*/
#include <stdio.h>
extern void asm_strcpy(const char *src, char *dest);
int main()
{
? const char *s = "seasons in the sun";
? char d[32];
? asm_strcpy(s, d);
? printf("source: %s", s);
? printf(" destination: %s",d);
? return 0;
}

?
ADS
中實現函數的匯編程序

;asm function implementation
AREA asmfile, CODE, READONLY
EXPORT asm_strcpy
asm_strcpy

loop
ldrb r4, [r0], #1 address increment after read
cmp r4, #0
beq over
strb r4, [r1], #1
b loop
over
mov pc, lr
END

?
在這里,C 和匯編之間的參數傳遞是通過ATPCS(ARM Thumb Procedure Call Standard)的規定來進行的。簡單的說就是如果函數有不多于四個參數,對應的用R0-R3來進行傳遞,多于4個時借助棧,函數的返回值通過R0來返回。
4.在匯編中調用C的函數
在匯編中調用C的函數,需要在匯編中IMPORT 對應的C函數名,然后將C 的代碼放在一個獨立的C 文件中進行編譯,剩下的工作由連接器來處理。
ADS
實現函數的C程序

;the details of parameters transfer comes from ATPCS
;if there are more than 4 args, stack will be used
EXPORT asmfile
AREA asmfile, CODE, READONLY
IMPORT cFun
ENTRY
mov r0, #11
mov r1, #22
mov r2, #33
BL cFun
END

/*C file, called by asmfile */
int cFun(int a, int b, int c)
{
return a + b + c;
}

?
在匯編中調用C 的函數,參數的傳遞也是通過ATPCS來實現的。需要指出的是當函數的參數個數大于4時,要借助stack,具體見ATPCS規范。
?
ARM匯編樣例: 使用匯編程序來控制LED
ARM采用內存映射的模式來使用I/O端口,因此CPU設備寄存器被映射到一個物理地址空間上.但是匯編中,對數字的運算全部是在通用寄存器中完成,因此LED對應的GPIO端口的值必須要裝入到通用寄存器中.
LDR指令用于從存儲空間(這里就是GPIO寄存器映射空間)裝入值到通用寄存器中,STR指令用于把計算的結果值存入到存儲空間上.
?
;定義端口E
寄存器預定義
rGPFCON?????????
EQU????????? 0x56000050???? ;
rGPFDAT ????????
EQU ??????? 0x56000054
rGPFUP ??????????
EQU ??????? 0x56000058????
?
;該偽指令定義了一個代碼段,
段名為Init ,
屬性只讀?????????????????????
???????????????????????????
AREA??????? Init,CODE,READONLY?????
???????????????????????????
ENTRY???????????????????????????????? ;
標識程序入口
???????????????????????????
CODE32???????????????????????????? ;
聲明32ARM指令
ResetEntry ?????
;下面這三條語句,
主要是用來設置IO
GPF7
為輸出屬性
???????????????????????????
LDR R0 , =rGPFCON??? ;將寄存器rPCONF
的地址存放到寄存器r0
???????????????????????????
LDR R1 , =0x4000
???????????????????????????
???????????????????????????
STR R1 , [R0 ]??????????? ;r1
中的數據存放到寄存器rPCONF
;下面這三條語句,
主要是禁止GPF
端口的上拉電阻
???????????????????????????
LDR R0 , =rGPFUP
???????????????????????????
LDR R1 , =0xffff
???????????????????????????
STR R1 , [ R0 ]
???????????????????????????
LDR R2 , =rGPFDAT??? ;將數據端口F
的數據寄存器的地址附給寄存器r2
LEDLOOP
???????????????????????????
LDR R1 , =0xff
???????????????????????????
STR R1 , [R2] ??????????????????? ;使GPE7
輸出高電平, D14
燈會滅
?
???????????????????????????
BL DELAY ??????????????????????????????????? ;調用延遲子程序
?
???????????????????????????
LDR R1 , =0x0
???????????????????????????
STR R1 , [R2 ]???????????????????? ;使GPE7
輸出低電平, D14
燈亮
???????????????????????????
BL DELAY ??????????????????????????????????? ;調用延遲
???????????????????????????
B LEDLOOP ?????????????????????????????? ;不斷的循環, D14
將不停的閃爍
;下面是延遲子程序
DELAY
???????????????????????????
LDR R3 , =0xBFFFF ????????????????? ;設置延遲的時間
DELAY1
???????????????????????????
SUB R3 , R3 ,#1????????????????????????? ; r3 =r3 -1
???????????????????????????
CMP R3 , #0x0 ?????????????????????????? ?;r3
的值與。相比較
???????????????????????????
BNE DELAY1???????????? ;比較的結果不為0 ( r3
不為0 ) ,
繼續調用delayl ,
否則執行下一條語句
MOV PC ,LR ;返回????????????????????????????????
???????????????????????????
???????????????????????????
END

七星彩走势图2元网官网