1.幾條彙編指令
1. LDR :(load)讀內存
LDR R0 , [R1] // 假設R1的值是x,讀取地址x上的數據,保存到R0中。
2. STR :(store)寫內存
STR R0 , [R1] // 假設R1的值是x,把R0的值寫到地址x上。
3. B:跳轉
halt: b halt // 此例用於實現死循環功能,避免程序跑飛。
4. MOV :(move)(移動) 賦值
LDR R0 , R1 // 把R1的值賦給R0,R0 = R1。
LDR R0 , #0x01 // 把立即數的值賦給R0,R0 = 0x01。
5. LDR :僞指令用法
LDR R0 ,= 0x12345678 // 把 0x12345678 這個值賦給R0,R0 = 0x12345678。
僞指令會被拆分爲幾條真正的arm指令執行,引入僞指令是因爲使用MOV指令賦值時真正值得小於寄存位寬。
2.彙編代碼
.text
.global _start
_start:
/*配置GPF4爲輸出引腳*/
/*把0x100寫到地址0x56000050*/
LDR r0,=0x56000050
LDR r1,=0x100 /* mov R0,#0X100 */
str r1,[r0]
/*設置GPF4爲輸出高電平*/
/*把0寫到地址0x56000054*/
LDR r0,=0x56000054
LDR r1,=0x00 /* mov R0,#0X00 */
str r1,[r0]
/* 死循環 */
mainloop:
b mainloop
.text和.global 是arm-gcc編譯器的關鍵詞。
.text 段保存代碼,是隻讀和【可執行】的,後面那些指令都屬於【.text】
段。
.global 告訴編譯器後續跟的是一個全局可見的名字【可能是變量,也可以是函數名】;
在本例中,_start是一個函數的起始地址,也是編譯、鏈接後程序的【起始地址】。由於程序是通過加載器來加載的,必須要找到 _start 名字的函數,因此_start必須定義成全局的,以便存在於編譯後的全局符合表中,供其它程序【如加載器】尋找到。
3.Makefile
led.bin:led.s
arm-linux-gcc -c -o led_on.o led_on.S
arm-linux-ld -Ttext 0 led_on.o -o led_on.elf
arm-linux-objcopy -O binary -S led_on.elf led_on.bin
/* 把可執行文件反彙編,此處用於分析代碼 */
arm-linux-objdump -D led_on.elf > led_on.dis
clean:
rm -f *.bin *.o *.elf
4.反彙編結果
可見其中的地址、機器碼和彙編碼。