arm常量編譯
採用交叉編譯的主要原因在於,多數嵌入式目標系統不能提供足夠的資源供編譯過程使用,因而只好將編譯工程轉移到高性能的主機中進行。
linux下的交叉編譯環境重要包括以下幾個部分:
1.對目標系統的編譯器gcc
2.對目標系統的二進制工具binutils
3.目標系統的標准c庫glibc
4.目標系統的linux內核頭文件
交叉編譯環境的建立步驟
一、下載源代碼 下載包括binutils、gcc、glibc及linux內核的源代碼(需要注意的是,glibc和內核源代碼的版本必須與目標機上實際使用的版本保持一致),並設定shell變數PREFIX指定可執行程序的安裝路徑。
二、編譯binutils 首先運行configure文件,並使用--prefix=$PREFIX參數指定安裝路徑,使用--target=arm-linux參數指定目標機類型,然後執行make install。
三、配置linux內核頭文件
首先執行make mrproper進行清理工作,然後執行make config ARCH=arm(或make menuconfig/xconfig ARCH=arm)進行配置(注意,一定要在命令行中使用ARCH=arm指定cpu架構,因為預設架構為主機的cpu架構),這一步需要根據目標機的實際情況進行詳細的配置,筆者進行的實驗中目標機為HP的ipaq-hp3630 PDA,因而設置system type為SA11X0,SA11X0 Implementations中選擇Compaq iPAQ H3600/H3700。
配置完成之後,需要將內核頭文件拷貝到安裝目錄: cp -dR include/asm-arm $PREFIX/arm-linux/include/asm cp -dR include/linux $PREFIX/arm-linux/include/linux
四、第一次編譯gcc
首先運行configure文件,使用--prefix=$PREFIX參數指定安裝路徑,使用--target=arm-linux參數指定目標機類型,並使用--disable-threads、--disable-shared、--enable-languages=c參數,然後執行make install。這一步將生成一個最簡的gcc。由於編譯整個gcc是需要目標機的glibc庫的,它現在還不存在,因此需要首先生成一個最簡的gcc,它只需要具備編譯目標機glibc庫的能力即可。
五、交叉編譯glibc
這一步驟生成的代碼是針對目標機cpu的,因此它屬於一個交叉編譯過程。該過程要用到linux內核頭文件,默認路徑為$PREFIX/arm-linux/sys-linux,因而需要在$PREFIX/arm-linux中建立一個名為sys-linux的軟連接,使其內核頭文件所在的include目錄;或者,也可以在接下來要執行的configure命令中使用--with-headers參數指定linux內核頭文件的實際路徑。
configure的運行參數設置如下(因為是交叉編譯,所以要將編譯器變數CC設為arm-linux-gcc): CC=arm-linux-gcc ./configure --prefix=$PREFIX/arm-linux --host=arm-linux --enable-add-ons 最後,按以上配置執行configure和make install,glibc的交叉編譯過程就算完成了,這里需要指出的是,glibc的安裝路徑設置為$PREFIXARCH=arm/arm-linux,如果此處設置不當,第二次編譯gcc時可能找不到glibc的頭文件和庫。
六、第二次編譯gcc
運行configure,參數設置為--prefix=$PREFIX --target=arm-linux --enable-languages=c,c++。
運行make install。
到此為止整個交叉編譯環境就完全生成了。
幾點注意事項
第一點、在第一次編譯gcc的時候可能會出現找不到stdio.h的錯誤,解決辦法是修改gcc/config/arm/t-linux文件,在TARGET_LIBGCC2_CFLAGS變數的設定中增加-Dinhibit_libc和-D__gthr_posix_h。
2. ARM架構的處理器定義一個常量佔多少個位元組是4個位元組嗎,例如,if(i=1),這個1占幾個位元組
一般前租:
32位的ARM架構,1佔4個位元組.
16位的ARM架構,1佔2個位元組.
可能還與慧巧兆i的定寬派義有關,最終由編譯器確定。
const定義的數據類型是常量,編譯器編譯後存儲在CODE(ROM、FLASH等)空間
4. 關於arm的數字常量
0X3FC=00000000,00000000,00000011,11111100,共32位。
0XFF =00000000,00000000,00000000,11111111,也是32位。
你看一下,0X3FC循環左移30位,也就是循環右移2位,是不是等於0XFF.
之絕猜閉前說的合法和不合法,指的是:
在32位的二進制中,第一個「1」和最後一個「1」,
跨度大於8位,就是不合法。(比如0x101,並裂分開來,跨度是9個位兆敗置了)
跨度小於8為,就合法。(比如0xF000000F,雖然本來是跨度很大的,如果循環左移或右移4位,跨度剛好是8個位置。)
我也是初學,不知道,說的明白沒有?
5. arm常用幾個匯編語言的程序
一。從一數到十
COUNT EQU 0x30003100 ;定義變數COUNT的基地址 AREA Example1,CODE,READONLY;聲明代碼段Example1為只讀 ENTRY ;標識程序入口
CODE32 ;聲明32位ARM指令 START LDR R1,=COUNT ;將0X30003100賦給R1 MOV R0,#0 ;執行R0=0
STR R0,[R1] ;存儲R0寄存器的數據到R1指向的存儲單元 LOOP LDR R1,=COUNT ;將0X30003100賦給R1
LDR R0,[R1] ;將R1中的數值作為地址,取出此地址中的數據保存到R0中 ADD R0,R0,#1 ;執行R0=R0+1
CMP R0,#10 ;將R0與10進行比較
MOVHS R0,#0 ;若R0大於等於10,則R0=0
STR R0,[R1] ;存儲R0寄存器的數據到R1指向的地址單元 B LOOP ;跳轉到LOOP
END ;匯編文件結束
二,9的8次冪
X EQU 9 ;初始化X為9 n EQU 8 ;初始化N為8
AREA Example3,CODE,READONLY ;生明代碼段Example3為只讀 ENTRY ;標識程序入口路
CODE32 ;聲明32位ARM指令
START LDR SP,=0x30003F00 ;把0x30003F00 賦給SP(R13) LDR R0,=X ;把9賦給R0 LDR R1,=n ;把8賦給R1
BL POW ;跳轉到POW,並把下一條指令地址存入到R14中 HALT B HALT ;等待跳轉
POW STMFD SP!,{R1-R12,LR} ;將R1-R12入棧,滿遞減堆棧 MOVS R2,R1 ;將R1賦給R2,並影響標志位 MOVEQ R0,#1 ;若Z=1,則R0=1
BEQ POW_END ;若Z=1,跳轉到POW_END MOV R1,R0 ;將R0中值賦給R1 SUB R2,R2,#1 ;將R2-1的只賦給R2 POW_L1 BL DO_MUL ;跳轉到DO-MUL,並把下一條指令地址存入R14中 SUBS R2,R2,#1 ;將R2-1的值賦給R2,並影響標志位 BNE POW_L1 ;若Z=0,跳轉到POW_L1
POW_END LDMFD SP!,{R1-R12,PC} ;數據出棧,存入到R1-R12,PC中 DO_MUL MUL R0,R1,R0 ;把R1*R0的值賦給R0 MOV PC,LR ;LR中的值賦給PC END ;匯編結束
三:從一一直加到一百
程序清單(一) C 語言實驗參考程序
#define uint8 unsigned char ;定義一個無符號字元常量uint8 #define uint32 unsigned int ;定義一個無符號整型常量unint32
#define N 100 ;定義一個常量N=100(宏定義,100用N代替) uint32 sum; ;定義sum為無符號整型常量(聲明一個unsigned int型的變數sum) void Main(void) ;主函數
{uint32 i; ;定義無符號整型常量i(聲明一個unsigned int型的變數i) sum=0; ;sum初始值為0
for(i=0;i<=N;i++) ;i在N內自增加1(i從0開始,i<=N時循環成立) {sum+=i;} ;把sum+i賦給sum while(1); ;為真循環 }
程序清單(二) 簡單的啟動代碼
IMPORT |Image$$RO$$Limit | ;R0輸出段存儲區域界線 IMPORT |Image$$RW$$Base | ;RW輸出段運行時起始地址 IMPORT |Image$$ZI$$Base | ;ZI輸出段運行時起始地址 IMPORT |Image$$ZI$$Limit | ;ZI輸出段存儲區域界線 IMPORT Main ;主函數
AREA Start,CODE,READONLY ;聲明代碼段start,為只讀 ENTRY ;程序入口
CODE32 ;聲明32位ARM指令 Reset LDR SP,=0x40003f00 ;將0x40003f00賦給SP
LDR R0,=|Image$$RO$$Limit| ;將R0輸出段存儲區域界線賦給R0 LDR R1,=|Image$$RW$$Base | ;將RW輸出段運行時起始地址賦給R1 LDR R3,=|Image$$ZI$$Base | ;將ZI輸出段運行時起始地址賦給R3 CMP R0,R1 ;比較R0和R1,相等Z=1,反之Z=0 BEQ LOOP1 ;若Z=1,則跳到LOOP1
LOOP0 CMP R1,R3 ;比較R1和R3,若R1<r3,c=0
LDRCC R2,[R0],#4 ;若C=0,讀取R0地址單元內容並且存入R2,且R0=R0+4 STRCC R2,[R1],#4 ;若C=0,讀取R2中的數據存入R1,且R1=R1+4 BCC LOOP0 ;若C=0,跳轉到LOOP0
LOOP1 LDR R1,=|Image$$ZI$$Limit| ;將ZI輸出段存儲區域賦給R1 MOV R2,#0 ;把0賦給R2
LOOP2 CMP R3,R1 ;比較R1和R3,若R1<r3,c=0 strcc="" r2,[r3],#4="" ;若c="0,將R2中數據保存到內存單元R3中,且R3=R3+4" bcc="" loop2="" b="" main="" ;跳轉到主程序="" end="" ;匯編結束=""
四、程序清單(一) C 語言調用匯編的參考程序
#define uint8 unsigned char ;定義一個無符號字元常量uint8 #define uint32 unsigned int ;定義一個無符號整型常量.uint32
extern uint32 Add(uint32 x,uint32 y); //聲明子程序Add為一個無符號整型常量,它為2個無符號整型常量x,y的和
uint32 sum; ;定義sum為無符號整型常量 void Main(void) ;無返回主程序
{sum=Add(555,168); ;sum等於555+168 while(1); ;為真循環 }
程序清單(二) 匯編加法函數程序
EXPORT Add ;聲明子程序Add方便調用 AREA Start,CODE,READONLY ;聲明代碼段start,為只讀 ENTRY ;程序入口
CODE32 ;聲明32位ARM指令
Add ADD R0,R0,R1 ;將R0+R1值賦給R0 MOV PC,LR ;將LR值賦給PC
6. arm編程與C語言的編程區別和方法
匯編主要是要了解CPU指令及用法,常說的是PC機的x86匯編,指令是x86的復雜指令集。緩沖
arm匯編是arm的精簡指令集,比x86容易學,程序格式倒是和x86匯編差不多。
C語言ARM的和x86的差不多,除了對硬體寄存器操作不同,其它語法和流程都一樣。
arm匯編程序每一行是指定arm
core執行一條指令,每條指令都是硬體相關。
如:LDR
R3,
#1
;用LDR指令將數值1放入R3寄存器准備參與運算
C語言與arm指令無關,只與邏輯運算有關,指定硬體地址的操作才與硬體相關;
如果用arm編譯器來編譯,每行可能編譯出1到多條arm指令。
如:i++;
//變數
i
遞增1等效於LDR
R3,#1
;
用LDR指令將數值1放入R3寄存器准備參與運算ADD
R2,
R2,
R3
;
用ADD指旁返令將R2、R3寄存器里的數值相加後放回R2寄存器以上等效匯編的R2、R3寄存器只是為了舉例,運哪飢C語言不像匯編,不需要由程序員指定用哪個寄存器參與運算,編譯器編譯時會根據程序結構自動判斷選擇。
無論是c語言還是匯編語言,編譯器編譯後的結果是機器執行碼,很多人因為匯編語言比較難懂及指令相關,所以以為它就是機器語言,其實它仍是人類設計的編寫程序的語言,仍需要編譯器編譯成機器碼才能執行,它只是比C語言更接近硬體而已。
7. 如何在IAR for ARM中定義常量或數組到flash而不是RAM中const,__flash關鍵字都不行,網上說加一個編譯開
我對你問題的理解:程序中的任何數組定義都辯廳慧攜答是暫存在Ram中的,如果你要寫到Flash中,斷電後還能保留的話?需要對硬體進行操作。看你用什麼硬體,查看該單片機的操作手冊,用戶使用手冊之類的,看對Flash怎麼操作,一般伏雹是通過配置寄存器,將數據寫到Flash中。
定義常量或數組直接到Flash,這個功能很好,如果真如網上所說加一個編譯就可以,那我也要學習學習。
8. 如何製作arm-linux-gcc編譯工具
一、下載源文件
源代碼文件及其版本:
binutils-2.19.tar.bz2, gcc-core-4.4.4.tar.bz2 gcc-g++-4.4.4.tar.bz2 Glibc-2.7.tar.bz2 Glibc-ports-2.7.tar.bz2 Gmp-4.2.tar.bz2 mpfr-2.4.0.tar.bz2mpc-1.0.1.tar.gz Linux-2.6.25.tar.bz2 (由於我在編譯出錯的過程中,根據出錯的信息修改了相關的C代碼,故而沒有下載相應的補丁)
一般一個完整的交叉編譯器涉及到多個軟體,主要包括bilinguals、cc、glibc等。其中,binutils主要生成一些輔助工具;gcc是用來生成交叉編譯器,主要生成arm-linux-gcc交叉編譯工具,而glibc主要提供用戶程序所需要的一些基本函數庫。
二、建立工作目錄
編譯所用主機型號 fc14.i686,虛擬機選的是VM7.0,Linux發行版選的是Fedora9,
第一次編譯時用的是root用戶(第二次用一般用戶yyz), 所有的工作目錄都在/home/yyz/cross下面建立完成,首先在/home/yyz目錄下建立cross目錄,然後進入工作目錄,查看當前目錄。命令如下:
創建工具鏈文件夾:
[root@localhost cross]# mkdir embedded-toolchains
下面在此文件夾下建立如下幾個目錄:
setup-dir:存放下載的壓縮包;
src-dir:存放binutils、gcc、glibc解壓之後的源文件;
Kernel:存放內核文件,對內核的配置和編譯工作也在此完成;
build-dir :編譯src-dir下面的源文件,這是GNU推薦的源文件目錄與編譯目錄分離的做法;
tool-chain:交叉編譯工具鏈的安裝位;
program:存放編寫程序;
doc:說明文檔和腳本文件;
下面建立目錄,並拷貝源文件。
[root@localhost cross] #cd embedded- toolchains
[root@localhost embedded- toolchains] #mkdir setup-dir src-dir kernel build-dir tool-chain program doc
[root@localhost embedded- toolchains] #ls
build-dir doc kernel program setup-dir src-dir tool-chain
[root@localhost embedded- toolchains] #cd setup-dir
拷貝源文件:
這里我們採用直接拷貝源文件的方法,首先應該修改setup-dir的許可權
[root@localhost embedded- toolchains] #chmod 777 setup-dir
然後直接拷貝/home/yyz目錄下的源文件到setup-dir目錄中,如下圖:
建立編譯目錄:
[root@localhost setup-dir] #cd ../build-dir
[root@localhost build -dir] #mkdir build-binutils build-gcc build-glibc
三、輸出環境變數
輸出如下的環境變數方便我們編譯。
為簡化操作過程。下面就建立shell命令腳本environment-variables:
[root@localhost build -dir] #cd ../doc
[root@localhost doc] #mkdir scripts
[root@localhost doc] #cd scripts
用編輯器vi編輯環境變數腳本envionment-variables:[root@localhost scripts]
#vi envionment-variables
export PRJROOT=/home/yyz/cross/embedded-toolchains
export TARGET=arm-linux
export PREFIX=$PRJROOT/tool-chain
export TARGET_PREFIX=$PREFIX/$TARGET
export PATH=$PREFIX/bin:$PATH
截圖如下:
執行如下語句使環境變數生效:
[root@localhost scripts]# source ./environment-variables
四、建立二進制工具(binutils)
下面將分步介紹安裝binutils-2.19.1的過程。
[root@localhost script] # cd $PRJROOT/src-dir
[root@localhost src-dir] # tar jxvf ../setup-dir/binutils-2.19.1.tar.bz2
[root@localhost src-dir] # cd $PRJROOT/build-dir/build-binutils
創建Makefile:
[root@localhost build-binutils] #../../src-dir/binutils-2.19.1/configure --target=$TARGET --prefix=$PREFIX
在build-binutils目錄下面生成Makefile文件,然後執行make,make install,此過程比較緩慢,大約需要一個15分鍾左右。完成後可以在$PREFIX/bin下面看到我們的新的binutil。
輸入如下命令
[root@localhost build-binutils]#ls $PREFIX/bin
9. 如何使用arm匯編指令實現結構化編程
ARM匯編程序特點:
l 所有運算處理都是發生通用寄存器(一般是R0~R14)的之中.所有存儲器空間(如C語言變數的本質就是一個存儲器空間上的幾個BYTE).的值的處理,都是要傳送到通用寄存器來完成.因此代碼中大量看到LDR,STR指令來傳送值.
l ARM匯編語句中.當前語句很多時候要隱含的使用上一句的執行結果.而且上一句的執行結果,是放在CPSR寄存器里,(比如說進位,為0,為負…)
CMP R0,R1
BNE NoMatch
比如上一句,BNE隱含的使用的上一句CMP執行結果.NE後綴表示使用Z標志位.兩句合起來的意思就是,如果R0,R1的值不相等,就跳轉到NoMatch處執行.
注意,PC=R15,CPSR=R16,
ARM偽指令不是必須的,但是一個完整沒有偽指令幾乎很難寫出來.
n 比如一個程序至少包含READONLY AREA和ENTRY,否則CPU都無法知道從哪裡開始運行
l ARM的屬於RISC,指令並不多,但是可以帶後綴表示擴展出不同用法,這里與X86匯編完全不同風格
n 如BNE實際上是B指令的變種,本質還同一類指令.只是多一個對CPSR的Z標志位的判斷。
ARM常用指令,偽指令
ARM常用指令並不太多,因此使用閱讀ARM匯編代碼,並不太困難.以下是使用頻率最高的指令和偽指令,並不是完整的指令集的教材。詳細指令參見參考資料。
l B,BL
l MOV,MVN
l LDR,STR
l ADD,SUB,ADC,SBC,MUL
l AND,ORR,XOR,TST,BIC
l CMP
l LDM/STM
l nop
1. 跳轉語句 B,BL
程序流程的跳轉,在 ARM 程序中有兩種方法可以實現程序流程的跳轉指令用於實現
l 使用專門的跳轉指令 B
l 直接向程序計數器PC 寫入跳轉地址值
n 這是幾乎是任何一種CPU必備的機器,PC表示CPU當前執行語句位置,改變PC的值,相當於實現程序跳轉
n 如實現類似C語言的Return 語句,就是用MOV PC,LR
n 這里可以在任意4G的空間進行跳轉
B指令(Branch)表示無條件跳轉.
B main ;跳轉到標號為main地代碼處
BL指令(Branch with Link)表示帶返回值的跳轉.
BL比B多做一步,在跳轉前,BL會把當前位置保存在R14(即LR寄存器),當跳轉代碼結束後,用MOV PC,LR指令跳回來,這實際上就是C語言執行函數的用法,
匯編里調子程序都用BL,執行完子函數後,可以用MOV PC,LR跳回來.
BL delay ;執行子函數或代碼段delay ,delay可以為C函數.
與MOV PC,XXX能在4G空間跳轉不同,B語句只能32M空間跳轉,(因為偏移量是一個有符號26bit的數值=32M)
2. 傳輸數據指令MOV,MVN
n MOV(MOVE)指令可完成從另一個寄存器、被移位的寄存器或將一個立即數載入到目的寄存器
MOV R0,R1 ; 把R1的值傳到R0
MOV R3,#3 ;把常數3傳給R3,MOV中用#表示常數,這個值不能超過
n MVN( MOVE Negative)取反後再傳值,比MOV多了一步取反
MVN R0, #0 ;把0取反(即-1)傳給R0
MVN R1,R2 ;把R2的值取反傳給R1
3. 載入/存儲指令,LDR,STR
n LDR,STR是用於寄存器和外部存儲器交換數據指令,注意與MOV的區別,後面只在寄存器或常數交換.
u LDR/STR可以採用多種定址方式,以下只舉出使用頻率最高幾種用法
n LDR(load)用於把一個32Bit的WORD數據從外部存儲空間裝入到寄存器中
LDR R0,[R1]; R1的值當成地址,再從這個地址裝入數據到R0 (R0=*R1)
LDR R1,=0x30008000 ; 把地址0x30008000的值裝入到R1中,LDR中用常數要用=打頭.(注意跟MOV的區別,MOV是#)
ldr r0, =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0)
用位與的方法賦值
n STR(Store) 用於把一個寄存器的值存入外部存儲空間,是LDR的逆操作.
STR R0,[R1] ; 把R0的值,存入到R1對應地址空間上(*R1 = R0)
STR R0,=0x30008000 ;把R0中值存入到地址0x30008000
S2C2440的中CPU內核以外的模塊的控制寄存器空間也是屬於外部空間,所以也得用如下指令LDR R0,=GPFDAT
4. 算術運算指令,ADD/ADC,SUB/SBC ,MUL
n ADD加法指令
ADD R0,R1,R2; R0=R1+R2
ADD R0,R1,#3 ;R0=R1+3
n ADC帶進位加法指令,即除了加兩個數以外,還要把CPSR的C值也要帶進來
u 通常用於大數(超過32Bit整數)相加,這時單用ADD不能處理,必須折成兩步,其中一步用ADC.
u 以下是做64Bit的加法
ADDS R0,R1,R2; R0=R1+R2,ADDS中S表示把進位結果寫入CPSR
ADC R5,R3,R4 ;R5=R3+R4+C
n SUB減法指令
SUB R0,R1,R2; R0=R1-R2
SUB R0,R1,#3 ;R0=R1-3
n SBC帶進位減法指令,即除了加兩個數以外,還要把CPSR的C值也要帶進來,類似ADC
u 以下是做64Bit的減法
SUBS R0,R1,R2; R0=R1-R2,SUBS中S表示把進位結果寫入CPSR
SBC R5,R3,R4 ;R5=R3-R4-C
n MUL 乘法指令
MUL R0,R1,R2; R0=R1*R2
MUL R0,R1,#3 ;R0=R1*3
5. 位操作指令 AND,ORR, TST,BIC
n AND位與指令
AND R0,R1,R2; R0=R1 & R2
AND R0,R1,#0xFF ;R0=R1 & 0xFF
n ORR位或指令
ORR R0,R1,R2; R0=R1 | R2
ORR R0,R1,#0xFF ;R0=R1 | 0xFF
n TST測試某一位是否為1,並把結果寫入CPSR,供下一句使用
TST R1,#0xffe; 等同於if(R1 & 0xffe)
TST R1,#%1;測試最低位是否為1,%表示二進制
n BIC清位操作
BIC R0,R0,#0xF ; 等同於 R0 &=~(0xF)
BIC R0,R0,#%1011 ; 該指令清除 R0 中的位 0 1 3,其餘的位保持; %表示是二進制,0x表示十六進制
6. 比較指令 CMP
n CMP比較兩個操作數,並把結果存入CPSR供下一句語句使用
CMP R0,R1; 比較R0,R1
7. 多寄存器語句傳輸指令,LDM,STM
類似於一次傳一個BUFFER到寄存器當中,或反過來.後面一般要接一個地址改變方法
n LDM 從BUFFER傳數據多個寄存器傳輸數據到
LDMIA R0! ,{R3-R9} ;加R0指向的地址上連續空間的數據,保存到R3-R9當中,!表示R0值更新,IA後綴表示按WORD遞增
LDMFD SP!,{R0-R7,PC}^;恢復現場,異常處理返回,^表示不允許在用戶模式下使用。
n STM 從寄存器列表向存儲空間傳值。
STMIA R1!,{R3-R9} ;將R3-R9的數據存儲到R1指向的地址上,R1值更新。
STMFD SP!,{R0-R7,LR}; 現場保存,將R0~R7,LR入棧
stmfd sp!,{r8-r9} ,把SP寄存器對慶的地址的值存到R8,R9當中.!表示最後的值寫入SP中。Fd表示
8. ARM指令的變形
大部分指令後位可以接 與S兩個特殊位來表示,對CPSR特殊的一些判斷
S,表示當前指令執行後把結果改寫CPSR
subs,Adds
取決於具體條件,只有CPSR滿足指定條件時才指這一指令
BEQ 實際上B+ EQ的條件執行.
addne 表示ADD +NE 才開始加.
9. ARM指令的定址方式
定址方式是根據指令中給出的地址碼來定位真實的地址,ARM中有9種定址方法
l 寄存器定址
直接用寄存器編號來定址,最為常用
MOV R1,R2 ;R2->R1
l 立即數定址
即指令中的地址碼是操作數本身,可以立即取出使用,立即數前帶一個#表示,否則表示一個地址
SUBS R0,R0,#1 ;R0 -1 ->R0
注意與SUBS R0,R0,1區別
l 寄存器偏移定址
這是ARM特有的定址模式,當第2操作數是寄存器,在執行操作之前,可以做一次移位操作
MOV R0,R2,LSL #3 ;R2的邏輯左移3位,結果放入R0,即R0=R2*8
ANDS R1,R1,R2,LSL R3;RS的值左移R3位,然後和R1相與操作,結果放入R1
移位操作有LSL (邏輯左移),LSR(邏輯右移) ,ASR(算術右移),ROR(循環右移)RRX帶擴展的循環右移
l 寄存器間接定址
即寄存器中值是一個地址,用[]來取出定位到地址當中
LDR R2,[R0] ;把R0的值當成地址,取出相應值,賦給R2
l 基址定址
把寄存器的地址值加上一個偏移量
LDR R2,[R3,#0x0F]; R3中的值加上0x0F,從這個地址取出值賦給R@
l 相對定址
基址定址的變形,由PC寄存器提供基準地址,指令中地址段作為偏移量.兩者相加即是有效地址,以下是BL採用相對定址
BL NEXT
…
NEXT
…
MOV PC,LR ;從子程序返回
10. ADS ARM的偽指令
類似於C語言的宏,由匯編程序預處理.
l 符號定義指令
全局變數定義 GBLA ,GBLL,GBLS
局域變數定義 LCLA,LCLL,LCLS
變數賦值SETA,SETL,SETS
其中上述偽指令中,最後面的A表示給一個算術變數賦值,L表示用於給一個邏輯變數賦值,s表示給一個字元串賦值
GBLL codedbg; 聲明一個全局的邏輯變數
Codebg SETL {TRUE} ; 設置變數為{TRUE}
LCLA bitno; 聲明一個算術變數
Bitno SETA 8 ;設變數值為8
l 數據定義偽指令
n SPACE 定義一個內存空間,並用0初始化
{label } SPACE expr
DataBuf SPACE 100 ;定義100位元組長空間, unsigned char DataBuf[100];
n DCB 定義一個連續位元組內存空間,用偽指令的表達式expr來初始化.一般可以用定義數據表格,或文字字元串.(這時等同於SETS),用於初始二進制BUFFER
{label} DCB expr{,expr …}
Dest DCB -120,20,36,55 ;等同於 unsigned char Dest[]={-120,20,36,55};
n DCU定義的一段字的內存空間(DCB是位元組),並用後面表達式初始化
_RESET DCU Reset ; 等同於 DWORD _RESET[]={Reset};
n MAP定一個結構化內存,相當於定義一個C結構
n FILED 定義一個結構化內存的成員
MAP 0x00,R9 ; 定義內存表,地址為R9
Timer FIELD 4 ; 定義數據域Timer,長為4字
Attrib FIELD 4 ; 定義數據域Attrib,長為4字
String FILED 100 ; 定義數據域String ,長為100字
相當於C語言的定義:
struct {
DWORD Timer ;
DWORD Attrib ;
Char String[100];
} R9;
11. 雜項的偽指令
n 位元組對齊 ALIGN
ALIGN; 聲明4位元組對齊
n 定義一個數字常量定義 EQU
NAME EQU expr {type}
PLLCON EQU 0xE01FC080;定義PLLCON,類似於C的宏或C++的常量
n 包含文件 GET和INCLUDE
INCLUDE lpc2106.inc
n NOP 空指令
在匯編時會被ARM的空操作代替,比如MOV R0,R0,一般用於延時與佔位。
n 聲明一個外部符符號 IMPORT,EXTERN
IMPORT,EXTERN 向外部導入一個符號,一般是外部程序全局變數
n 條件編譯:[]。類似於C的#ifdef 之類定義。
格式 :[ 條件表達式
滿足條件分支
|
不滿足條件分支
]
示例1:
[ ENTRY_BUS_WIDTH=32 ;類似#if ENTRY_BUS_WIDTH=32
b ChangeBigEndian ;DCD 0xea000007
] ; 類似#endif
示例2: [ CLKDIV_VAL>1 ; 類似#if CLKDIV_VAL>1
bl MMU_SetAsyncBusMode
|;類似#else
bl MMU_SetFastBusMode ; default value.
]; 類似#endif
示例3 [ THUMBCODE 類似#ifdef THUMBCODE
bx lr
| ;類似#else
mov pc,lr
] ;類似#endif
n 段定義 AREA
n 指令集定義 CODE16和CODE32
指示是Thumb 指令集(壓縮指令集,每個指令16位)。還是普通32位指令集
n 匯編結束:END
n 程序入口ENTRY
10. Arm編譯器有什麼用
Arm RVDS 4.1中的Arm編譯器是唯一一個與Arm編譯器聯合開發的商用編譯器,專門設計用於為 Arm 編譯器架構程序並提供最優支持。該編譯器的開發歷經有20年左右,被公認為是業界標准 C 和 C++ 編譯器,用於生成面向 Arm、Thumb、Thumb-2、VFP 和 NEON 指令集的應用程序。詳情請到億道電子咨詢