入門匯編語言的五大技巧
編程是門藝術,大多數(shù)開發(fā)者實際工作中只是開發(fā)App,正常來說是不會接觸到匯編的,主要有兩大原因,一來編譯語言不容易學習,二來,日常生活中比較少用到。
匯編語言是最古老的編程語言,在所有的語言中,它與原生機器語言最為接近。它能直接訪問計算機硬件,要求用戶了解計算機架構和操作系統(tǒng)。學習匯編最大的用處就是可以幫助我們更好地理解高級語言,因此還是很有必要的,
本文使用RISC-V 為例來向大家展示,來如何使用編寫語言設計程序邏輯,并最終將程序邏輯轉(zhuǎn)換為匯編語言的程序。
用合適的語言設計邏輯
這是最難的一步,許多學生想直接編寫完整的功能模塊的軟件包。但是,如果你不喜歡匯編,那么這是一種注定要失敗的方法,相反,為了把邏輯從語言中分離出來,我們必須用我們能理解的語言來寫。
如果一個學生不懂C語言或一些低級語言,那么我建議他們用偽代碼來寫。太高級的語言編譯困難,而太低級的語言又會講邏輯設計困難,所以,推薦使用C/C++或其他類似的語言。
在翻譯時,有些編輯器可以把它們并排放在一起,這是很有幫助的。因為在大腦中保留一份指令列表是很困難的,特別是當你在編譯一個復雜的程序時。
一步一個腳印
許多學生試圖從頭到尾編寫整個程序,而中間沒有測試任何內(nèi)容。如果是初學者,我建議用增量式編程,關鍵是在完成一部分邏輯時進行測試。這可以像完成一個for循環(huán)就進行測試。
測試的一種方法是將C/C++程序與匯編程序連接在一起,你可以通過在C++中創(chuàng)建函數(shù)程序集的原型并在兩者之間切換來實現(xiàn)這一點。你需要確保兩者是不同的,否則鏈接會出錯,按照一般的做法通常會在C函數(shù)前面加上一個“c”來區(qū)分。我們可以調(diào)用Show來運行匯編語言編寫的函數(shù):
- extern "C" { // Turn off name mangling
- void show(int *x);
- }
extern " C "將告訴c++函數(shù)遵循C的"調(diào)用約定"。我們真正關心的是關閉名稱修改,這樣我們就可以創(chuàng)建一個名為“show”的標簽,并擁有我們的函數(shù)。
了解匯編語言的功能定位
正如巨石強森(Dwayne Johnson)常說的那樣:“認清自己的角色”。知道C/ C++為我們做了什么和程序集沒有為我們做什么是很重要的。。例如,4 + 3 * 4將自動將運算排序為先執(zhí)行乘法,再執(zhí)行加法。然而,在匯編中,我們必須先選擇乘法指令,然后再選擇加法指令。
知道如何調(diào)用函數(shù)
大多數(shù)ISA架構都會附帶調(diào)用約定手冊,比如ARM和RISC-V。這些只是為在所有語言中調(diào)用函數(shù)制定了一些基本規(guī)則。不過幸運的是RISC-V寄存器的 “ABI” 命名規(guī)則,有助于程序員理解它們的含義。比如:
- 整數(shù)參數(shù)在寄存器 A0-A7 中,浮點參數(shù)在寄存器 FA0-FA7 中
- 通過對堆棧指針的 sub 操作去分配函數(shù)堆棧。在調(diào)用完成后使用 add 操作進行銷毀
- 堆棧大小必須以 8 的整數(shù)倍形式分配
- 所有參數(shù)和臨時寄存器必須在函數(shù)調(diào)用后,被視為銷毀態(tài)
- 在函數(shù)調(diào)用之后,已保存寄存器才能被顯式保存。如果使用了任何已保存的寄存器,則必須在函數(shù)返回之前還原它們的原始值
- 通過 a0 寄存器做為返回值,將數(shù)據(jù)返回給調(diào)用方。
- .global main
- main:
- addi sp, sp, -8
- sd ra, 0(sp)
- la a0, test_solve
- call solve
- mv a0, zero
- ld ra, 0(sp)
- addi sp, sp, 8
- ret
你可以從上面的代碼中看到,我們首先分配我們的堆??蚣埽4嫠行枰4娴募拇嫫?,執(zhí)行,然后在返回之前撤消的所有寄存器。
文檔
用C或其他語言編寫匯編代碼會讓你為每一行C代碼編寫多行匯編代碼。如果你試圖調(diào)試程序,這可能會讓你有些難度,所以,我總是寫C代碼作為匯編的注釋,然后把它拆開,并展示我做它的每一步。
你可以從上面的代碼中看到,我有原始的C代碼(第一個注釋),然后對每個片段進行內(nèi)聯(lián)注釋。這樣的方式使我們能夠保證程序可以正確地執(zhí)行每一步。