原文: https://blog.stephenmarz.com/2021/05/12/tips-to-writing-assembly/
當(dāng)用戶(hù)使用 C、C++ 或任何其他高級(jí)語(yǔ)言時(shí),編譯器決定了程序的藝術(shù)。然而這一次,我們將自行面對(duì) CPU,新手實(shí)現(xiàn)面向指令集編程的藝術(shù),本文使用RISC-V 為例來(lái)向大家展示,來(lái)如何使用編寫(xiě)語(yǔ)言設(shè)計(jì)程序邏輯,并最終將程序邏輯轉(zhuǎn)換為匯編語(yǔ)言的程序。
嵌入式專(zhuān)欄
1
用合適的語(yǔ)言設(shè)計(jì)邏輯對(duì)于初學(xué)者來(lái)說(shuō),這是最難的一步,因?yàn)楝F(xiàn)在學(xué)生們常用的高級(jí)語(yǔ)言中,往往使用更抽象的工具進(jìn)行邏輯設(shè)計(jì),許多學(xué)生想直接編寫(xiě)完整的功能模塊。但是對(duì)于匯編語(yǔ)言來(lái)說(shuō),這是一種注定要失敗的方法。相反,為了把邏輯和語(yǔ)言分開(kāi),我們必須用我們理解的語(yǔ)言來(lái)設(shè)計(jì)程序邏輯。
如果一個(gè)學(xué)生不懂 C 或一些“低級(jí)”語(yǔ)言,那么我建議他們用偽碼寫(xiě)。因?yàn)橛锰呒?jí)的語(yǔ)言如 Java 其實(shí)是面向虛擬機(jī)編程的,這會(huì)增加程序邏輯到匯編語(yǔ)言的轉(zhuǎn)換難度,而使用太低級(jí)的語(yǔ)言會(huì)使程序邏輯設(shè)計(jì)變得困難。因此,我推薦 C 或 C++,在將代碼進(jìn)行轉(zhuǎn)換的時(shí)候,最好將有對(duì)應(yīng)的代友橫向放在一起,一些編輯可以把它們并排放在一起,這是很有幫助的。
說(shuō)實(shí)話(huà)這部分雖然看似平平無(wú)奇,但卻讓我頗感到意外,因?yàn)?C 語(yǔ)言和匯編的對(duì)照完全可以通過(guò) gcc 的 -o -g 參數(shù)以及 objdump 實(shí)現(xiàn)。
87f27431c5eb365ac14f4b0958ea44ab.png (82.87 KB, 下載次數(shù): 34)
下載附件
保存到相冊(cè)
2021-5-31 15:51 上傳
在譯者的理念中,匯編語(yǔ)言是專(zhuān)門(mén)用于填補(bǔ)空白,只有當(dāng)其它語(yǔ)言不能勝任的時(shí)候才會(huì)考慮用匯編語(yǔ)言上場(chǎng)工作,不過(guò)從這篇博文中透露出的信息來(lái)看,國(guó)外在教學(xué)過(guò)程中對(duì)于匯編語(yǔ)言的運(yùn)用范圍也是不設(shè)限的,而在譯者印象中能用匯編語(yǔ)言實(shí)現(xiàn)任何功能的程序員,在國(guó)內(nèi)只有求伯君,嚴(yán)援朝等廖廖數(shù)人而已,由此可見(jiàn)我們?cè)?IT 基礎(chǔ)教育領(lǐng)域要做的工作還很多。
嵌入式專(zhuān)欄
2
小步快跑,不要試圖一口吃個(gè)胖子很多匯編語(yǔ)言的初學(xué)者試著從頭到尾寫(xiě)完整的程序,而沒(méi)有在中間進(jìn)行過(guò)任何測(cè)試關(guān)鍵,但是我建議在完成部分邏輯時(shí)就立刻進(jìn)行測(cè)試。這樣做其實(shí)很簡(jiǎn)單,比如完成了一個(gè) for 循環(huán),等等一小部分功能就要開(kāi)始測(cè)試。
可以將 C 或 C++ 程序與匯編程序連接起來(lái)。通過(guò)在 C++ 中原型化組裝函數(shù)的名稱(chēng)實(shí)現(xiàn)這一點(diǎn)。按照一般的做法通常會(huì)在 C 函數(shù)前面加上一個(gè) “c” 來(lái)區(qū)分。我們可以調(diào)用 Show 來(lái)運(yùn)行匯編語(yǔ)言編寫(xiě)的函數(shù)。
其實(shí)這部分的建議并不僅僅針對(duì)于 RISC-V 甚至不是針對(duì)匯編語(yǔ)言,無(wú)論是什么語(yǔ)言的編程,當(dāng)你想到要進(jìn)行單元測(cè)試的時(shí)候往往就已經(jīng)晚了,隨時(shí)對(duì)于一個(gè)細(xì)小的模塊進(jìn)行測(cè)試真的是一個(gè)好習(xí)慣。
嵌入式專(zhuān)欄
3
了解匯編語(yǔ)言的功能定位這里我們必須要充分認(rèn)識(shí)到?jīng)]有匯編語(yǔ)言和有編譯器解釋器的高級(jí)語(yǔ)言真的完全不一樣,不養(yǎng)兒不知父母恩,不寫(xiě)匯編不知各類(lèi)語(yǔ)言之父有多神。在匯編語(yǔ)言中操作順序都需要程序員自己去掌握。
例如,4+3*4 的運(yùn)算,作何一種語(yǔ)言的編譯器都先執(zhí)行乘法,然后再加法。然而在匯編語(yǔ)言的編程世界中,我們必須首先選擇乘法指令,然后再選擇加法指令。沒(méi)有為我們進(jìn)行運(yùn)算符號(hào)的優(yōu)先級(jí)重排。
嵌入式專(zhuān)欄
4
了解如何調(diào)用函數(shù)在匯編語(yǔ)言中編寫(xiě)一個(gè)函數(shù)是一項(xiàng)非常艱苦的任務(wù),大多數(shù) ISA 體系結(jié)構(gòu)(如ARM和RISC-V)的芯片都將附帶專(zhuān)門(mén)的工作手冊(cè),當(dāng)然這些手冊(cè)中只是制定了一些基本規(guī)則,如何傳遞參數(shù),如何接收返回結(jié)果,又如何構(gòu)造函數(shù)棧禎等等具體的話(huà)題都值得深入討論。
不過(guò)幸運(yùn)的是 RISC-V 寄存器的 “ABI” 命名規(guī)則,有助于程序員理解它們的含義。比如: 整數(shù)參數(shù)在寄存器 A0-A7 中,浮點(diǎn)參數(shù)在寄存器 FA0-FA7 中 通過(guò)對(duì)堆棧指針的 sub 操作去分配函數(shù)堆棧。在調(diào)用完成后使用 add 操作進(jìn)行銷(xiāo)毀 堆棧大小必須以 8 的整數(shù)倍形式分配 所有參數(shù)和臨時(shí)寄存器必須在函數(shù)調(diào)用后,被視為銷(xiāo)毀態(tài) 在函數(shù)調(diào)用之后,已保存寄存器才能被顯式保存。如果使用了任何已保存的寄存器,則必須在函數(shù)返回之前還原它們的原始值 通過(guò) a0 寄存器做為返回值,將數(shù)據(jù)返回給調(diào)用方。
以下面這段代碼為例: .global mainmain:addi sp, sp, -8sd ra, 0(sp)la a0, test_solvecall solvemv a0, zerold ra, 0(sp)addi sp, sp, 8ret我們可以看到先通過(guò) addi sp,sp,-8 的語(yǔ)句構(gòu)造函數(shù)禎,保存所有寄存器后執(zhí)行相應(yīng)函數(shù)邏輯,接下來(lái)將所有包括sp寄存器內(nèi)的調(diào)用環(huán)境恢復(fù),最后返回。
嵌入式專(zhuān)欄
5
文檔建議使用 C 或其他語(yǔ)言編寫(xiě)匯編語(yǔ)言的注釋?zhuān)靡韵麓a為例:
# used |= 1 << ( x[i * 9 + col] - 1) li t0, 9
mul t1, s3, t0 # t1 = i * 9
add t1, t1, s2 # t1 = i * 9 + col
slli t2, t1, 2 # Scale by 4
add t2, t2, s6 # x + i * 9 + col
lw t3, 0(t2) # x[i * 9 + col]
addi t3, t3, -1 # x[i * 9 + col] - 1
li t4, 1
sll t4, t4, t3 # 1 << x[i * 9 + col] - 1
or s5, s5, t4 # used |= ...
原始 C 語(yǔ)言代碼進(jìn)行一個(gè)總的注釋?zhuān)缓髮⒚總(gè)匯編語(yǔ)言片段的也使用 C 語(yǔ)言進(jìn)行注釋。尤其當(dāng)涉及到運(yùn)算操作順序時(shí),這樣的方式使我們能夠保證程序可以正確地執(zhí)行每一步。
免責(zé)聲明:本文素材來(lái)源網(wǎng)絡(luò),版權(quán)歸原作者所有。如涉及作品版權(quán)問(wèn)題,請(qǐng)與我聯(lián)系刪除。
|