iT邦幫忙

2021 iThome 鐵人賽

DAY 4
0
自我挑戰組

如何從 AVR 當中探索計算機的本質系列 第 6

Day6 — 組合語言淺談

  • 分享至 

  • xImage
  •  

對於組合語言,最大的迷思在於:「有了編譯器,為什麼我還要學組合語言?」,關於學習組合語言的好處我認為可以分成幾點討論。

1. 知道處理器(MCU)是如何運作的

由於編譯器會產生一些 bootstrap 程式碼,所以光看編譯器編譯後的程式碼,可能會比想像中的數量還多。為了深入底層,你可能還需要知道編譯器對於特定片段的程式碼是如何編譯的。

2. 簡單直覺

一旦了解組合語言的概念,其實它並沒有想像中那麼難理解。因為我們是直接撰寫組合語言,所以也不會有編譯器轉換時的隔閡。你也不需要再去學習各種語言特性,組合語言運作的方式不會因為 chip 不同而有變化。

學習組合語言不代表你必須要精通它,或是從此以後只能用組合語言撰寫,回頭看看人類發展高階語言的原因,正是因為組合語言的表達能力有限,繁瑣的操作也會造成開發人員的心智負擔,不過知道組合語言是如何運作的,能夠幫助你在使用高階語言撰寫程式時,對實際上電腦會如何運作有更深的了解。

因此,如果你真的想更了解 AVR 的架構,認識組合語言就是必經的一段過程。


語法

組合語言通常會由三個部分組成:操作、運算數 1(Operand)、運算數 2。舉例來說,在 AVR 當中若要將數字載入至暫存器當中可以這樣寫:

ldi r16, 00000001

在這段程式碼中,ldi 代表操作,在這裡指得是 load immediately,r16 為暫存器,00000001 則為數字。我們通常會將操作稱為指令(instruction),在 AVR 當中大部分的 instruction 都是一個 cycle。

在這邊要特別注意的是,並不是每個指令都能夠像這樣,後面直接接一個常數,而是要先將數字載入到暫存器之後,再繼續接下來的操作。

例如我希望計算 1 + 1 後將結果放入暫存器中,這樣寫是不對的:

add r16, 1, 1

而是需要先將 1 載入到暫存器中,再進行加法:

ldi r14, 1
ldi r15, 1
add r15, r14

定址模式(addressing mode)

在 AVR 當中有許多定址模式,最主要的目的在於簡化在記憶體(RAM)與暫存器之間的資料交換與操作。

1. Register Direct:single register

一個指令搭配一個暫存器,例如 inc 指令

inc r16

將暫存器 r16 增加 1。在 AVR 組合語言當中,通用暫存器會以 r 當作前綴加上數字表示,assembler 會對照 AVR 的架構將這些暫存器映射到對應的位址(Register File)。在 AVR 當中有 32 個暫存器,所以 r0 ~ r31 都是可使用的暫存器。

2. Register Direct: 2 registers

可使用兩個暫存器,如 add 指令

add r16, r17
mov r0, r1

3. I/O Direct

可以直接在暫存器與 I/O Port 間傳輸資料,最常見的就是 inout 指令:

in r16, PINB ; 將 PINB 的資料傳給暫存器 r16 
out PORTC, r16 ; 將 r16 的資料傳給 PORTC

在這邊可以發現 PINB 以及 PORTC 之類看起來很像變數的東西,這是由 assembler 事先定義的變數。在 AVR 當中除了通用暫存器(general purpose register)這類可以讓我們直接操作的暫存器之外,還有控制各種功能、參數的暫存器可以使用。

https://ithelp.ithome.com.tw/upload/images/20211002/20103565tMNFcLnjw7.png

在 AVR 當中暫存器可以透過特定的位址存取,舉例來說 PORTB 這個暫存器就存在於 0x18 這個記憶體位址當中。不過直接撰寫記憶體位址撰寫時比較麻煩,因此 assembler 通常會事先定義好這些暫存器的位址與名稱,在撰寫時就不需要查表寫記憶體位址了。

4. Data Direct

將資料寫到 data space 當中。在 AVR 當中 data space 包含 Register file、I/O memory、SRAM。例如 sts 指令

sts 0x1000, r16

將 r16 的資料寫入到 data space 的位址 0x1000

AVR 採用的是哈佛架構(Harvard Architecture),在哈佛架構當中,程式指令和資料儲存會分別存放在不同的記憶體空間。目前使用哈佛架構的微控制器與中央處理器的晶片有 AVR、ARM9、ARM10、ARM11。

因此我們將資料儲存的地方稱為 data space,儲存指令的地方稱為 program memory space。將程式指令與資料儲存空間分開存放最大的好處在於,當我們在執行指令時,就可以預先讀取下一條指令,進而提高效能。

5. Data Indirect

間接定址。在 AVR 當中有三個比較特別的暫存器稱為 X, Y, Z 暫存器。我們在前面有講到,AVR 具有 32 個通用暫存器,分別從 r0 ~ r31,其中 r26 ~ r31 在特定指令中會具有定址功能,而當這些暫存器當作定址功能使用時就稱為 X、Y、Z 暫存器。

LD r16, Y

這個指令的意思是以 Y 的值當作記憶體位址,找出此記憶體位址儲存的值,再將值放入 r16 暫存器當中。因為不是直接將值放入暫存器中,而是先找記憶體位址再去找值,因此才有 indriect 之稱。從這個指令多少就能感受出指標在組合語言的表達是什麼,其實就是 indirect 的對應。

除此之外 AVR 還有提供執行後增、減的功能。

LD r16, Y+

當執行完這條指令後,將 Y 暫存器的值 +1,這樣在存取連續的記憶體空間時很方便。

ST Y+, r28

這條指令則是將暫存器 r28 的內容存入到以 Y 暫存器的值為位址的記憶體。

6. Data Indriect with displacement

可以在 X、Y、Z 當中自行加入常數的 offset

LDD r16, Y+0x10

將 Y 的值加上 0x10 後當作記憶體位址去尋找對應的值。

7. Program memory addressing

可以使用 Z 暫存器當作記憶體位址存取到 program memory。

LPM

8. Indriect program addressing

使用 ijump 或是 icall 時可根據 z-register 的值改寫 PC 的位址。

PC 指得是 program counter,在 MCU 當中會不斷從 flash memory 當中獲取下一條指令解碼後執行,為了得知目前執行到哪一條指令,通常會使用 program counter 來儲存,每次執行一條指令時就將 PC+1。PC 可透過 ijump、icall 修改,讓程式跳到指定的位置後執行,進而實現像是迴圈、條件式判斷、函數呼叫等功能。

9. Relative Program Addressing

以 ijump 與 icall 都是直接以記憶體位址改動 PC 值,也可以透過 offset 的方式來改變。

rjmp r16
rcall r16

總結

AVR 的指令集雖然不多,但算下來也有 100 多條,文章裡無法一一介紹,不過我會在接下來介紹到特定功能時,一併介紹對應的指令。


上一篇
Day5 — 前導:使用工具介紹
下一篇
Day7 — GPIO 功能
系列文
如何從 AVR 當中探索計算機的本質7
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言