原文地址:http://thinkingeek.com/arm-assembler-raspberry-pi/
閱讀建議:盡量閱讀原文
一般來講扁藕,學(xué)習(xí)高級語言要比學(xué)習(xí)匯編有用的多。但學(xué)習(xí)一些匯編也是有好處的疚脐。本系列文章的目的并不是要去精通匯編語言編程亿柑,而是作為一個入門教程,希望大家通過學(xué)習(xí)可以了解一些底層的細(xì)節(jié)棍弄。
ARM介紹
下面只是介紹一些常用的細(xì)節(jié)望薄,并不全面。
ARM是32位結(jié)構(gòu)并遵循一個簡單的設(shè)計原則:flexibility(靈活)呼畸。這對于集成商(芯片廠商)來講是非常方便的(在設(shè)計硬件的時候有非常大的自由)痕支,但對于系統(tǒng)開發(fā)者特別是需要考慮硬件多樣性的開發(fā)者來講就不那么友好了。教程中的代碼等都在 樹莓派 硬件平臺驗證過蛮原。
雖然有些部分可以在其他ARM平臺運(yùn)行卧须,但有些只能在 樹莓派 上運(yùn)行,教程中并未進(jìn)行區(qū)分儒陨。參考:ARM官網(wǎng)
書寫匯編程序
匯編語言只是基于機(jī)器碼實現(xiàn)的非常簡單的語法規(guī)則花嘶。
機(jī)器碼(二進(jìn)制碼)是只機(jī)器可執(zhí)行的序列。是指令的集合蹦漠,指令是被編碼成二進(jìn)制形式(具體的編碼規(guī)則可參考ARM教程)椭员。當(dāng)然直接使用二進(jìn)制指令編寫代碼是非常費(fèi)力的。
因此我們使用ARM匯編語言編寫程序笛园。由于計算機(jī)無法直接執(zhí)行匯編程序隘击,因此我們使用 匯編器 將其轉(zhuǎn)換成可直接執(zhí)行的機(jī)器碼序列。匯編器又稱為 GNU匯編器研铆,原因是其屬于GNU項目的一部分埋同。
第一個匯編程序
下面我們開始寫第一個匯源程序,非常簡單蚜印,只是返回一個錯誤碼莺禁。
/* -- first.s */
/* This is a comment */
.global main /* 'main' is our entry point and must be global */
.func main /* 'main' is a function */
main: /* This is main */
mov r0, #2 /* Put a 2 inside the register r0 */
bx lr /* Return from main */
Create a file called first.s
and write the contents shown above. Save it.
打開一個文本編輯器如vim,新建一個匯編源文件first.s (匯編文件一般都以.s結(jié)尾)窄赋。輸入上面代碼哟冬,保存退出楼熄。
執(zhí)行下面命令:
$ as -o first.o first.s
命令執(zhí)行后會產(chǎn)生first.o。現(xiàn)在鏈接成可執(zhí)行程序:
1$ gcc -o first first.o
如果一切順利我們會得到一個可執(zhí)行程序first浩峡】善瘢拷貝到目標(biāo)平臺執(zhí)行:
./first
沒有任何輸出,如下命令獲取錯誤碼:
$ ./first ; echo
$ 2
錯誤碼2就是上面代碼賦值的翰灾。將下面編譯腳本保存一下缕粹,后續(xù)就不需要每次手動編譯了。
# Makefile
all: first
first: first.o
gcc -o $@ $+
first.o : first.s
as -o $@ $<
clean:
rm -vf first *.o
代碼解釋
上面代碼實現(xiàn)了一個c的main函數(shù)纸淮,只做了“返回2”的動作平斩。并且使用c的runtime幫我們實現(xiàn)了應(yīng)用程序的初始化和退出的工作。后續(xù)的示例代碼都使用這種方式咽块。
下面一行一行解釋绘面。
1 /* -- first.s */
2 /* This is a comment */
這些是注釋,注釋是包括在/**/中的內(nèi)容侈沪。匯編器會忽略這些內(nèi)容揭璃,但一般不要內(nèi)嵌。
3 .global main /* 'main' is our entry point and must be global */
這是一個GNU匯編器指令亭罪。匯編器指令用于控制匯編行為瘦馍。以 "." 開頭,后面跟指令的名字以及一些參數(shù)应役。上述代碼表示main是一個全局變量情组,只有這樣c runtime才能調(diào)用到main函數(shù)。如果不聲明為全局的扛吞,c runtime將不能調(diào)用進(jìn)而鏈接過程會失敗
4 .func main /* 'main' is a function */
這又是另一個匯編器指令呻惕,將main聲明為函數(shù)。由于匯編程序一般包含指令(代碼)和數(shù)據(jù)滥比,因此我們需要顯示的將main聲明為函數(shù)亚脆,即代碼。
6 main: /* This is main */
匯編代碼如果不是匯編器指令一般都是如下形式:label:instruction
空白行會被忽略盲泛。A line with onlylabel:, applies that label to the next line (you can have more than one label referring to the same thing this way).
instruction表示匯源源代碼濒持,上面的代碼只定義了main label沒有代碼。
7 mov r0, #2 /* Put a 2 inside the register r0 */
前面的空白字符會被忽略寺滚,但縮進(jìn)格式表示下面代碼屬于main函數(shù)柑营。上面代碼是mov指令,表示設(shè)置寄存器r0的值為2村视。下一章會涉及到更多的寄存器官套,還會涉及到立即數(shù)等,在arm指令中,目標(biāo)參數(shù)一般都在左邊奶赔。
8 bx lr /* Return from main */
bx指令表示“branch-跳轉(zhuǎn) 和 exchange-變換”惋嚎,這里先不關(guān)心變換。跳轉(zhuǎn)指令會改變指令的執(zhí)行流程站刑。arm處理器指令的執(zhí)行方式是順序的另伍,一條接著一條,因此執(zhí)行完mov指令后绞旅,會執(zhí)行bx摆尝。跳轉(zhuǎn)指令用于顯示的修改執(zhí)行序列,上面的指令的含義是跳轉(zhuǎn)到lr寄存器指示的位置繼續(xù)執(zhí)行因悲。暫時我們先不關(guān)心lr的內(nèi)容堕汞,只需知道bx使得程序跳出了main函數(shù),并結(jié)束了程序的執(zhí)行晃琳。
簡單介紹下錯誤碼(error code)臼朗,main函數(shù)執(zhí)行的結(jié)果就是這個應(yīng)用的錯誤碼,當(dāng)程序執(zhí)行完畢蝎土,錯誤碼需要保存在r0寄存器中。