x86組合語言- 第一章| 組合語言觀念

文章推薦指數: 80 %
投票人數:10人

西瓜靠大邊,跟大家一起用Intel 慣例的寫法就好。

像上面mov eax, 4 這樣子的指令形式,都叫「組合語言」,說穿了只是把當初的「x86 機器 ... Thiswebsite Archives Categories Tags About Donate RSS 歡迎!您似乎正在使用廣告攔截器。

請考慮通過禁用您的廣告攔截器來支持我。

J.J.Huang   2019-07-01   x86組合語言   瀏覽次數:次   {{moment(1561942800000).fromNow()}} x86組合語言-第一章|組合語言觀念 程式、技術越來越多,但是我在學習的過程中,發現會一直回去研究、探討、學習基礎的知識。

因為前面的CE教學,發現組合語言在後面會需要一些基礎,於是乎上網找了些文章,發現很多啊⋯。

下面這些內容是2010年的資料了,我覺得內容不錯還是有參考價值;基本上是部分轉載,只是希望自己有概念,建議閱讀過稍微了解即可。

機器語言與80x86大家家裡用的計算機器叫做個人電腦(PC),可以拿來安裝Windows、Linux,甚至MacOSX…等作業系統。

個人電腦的CPU演變歷史,可以說就是Intel的歷史。

從最早的16位元CPU:「8088/8086->80286」,再演化到32位元的80386、80486…後來因為商標不能用數字註冊,Intel不使用80586命名,從586開始,改名為歷史上的PentiumCPU。

AMD也差不多是在Pentium時代開始慢慢成為Intel在個人電腦處理器上的競爭者。

可以想見Intel就是個人電腦處理器的「唯一制定者」,Intel自己做新的CPU也要向後相容以前的東西,就像Windows7也得要能執行WindowsXP的程式一般。

你在286寫的程式,拿去給486的CPU也要能跑。

所以「個人電腦CPU=x86家族」…好啦,可能有人不認同這句話。

你跟美國人講話就要講英文、跟法國人講法文;跟x86家族的處理器講話,就要講「x86機器語言」;跟Intel8051單晶片處理器溝通,就講「8051機器語言」;在算盤本裡面介紹的處理器是「MIPS」家族,就用「MIPS機器語言」跟其溝通。

所有處理器裡面,x86家族功能當然是最強,每一代都有增加新功能,又要向後相容,所以其實該語言最複雜、不規則、不好學。

但只用些基本的功能的話,還是過得去的。

組合語言—IntelStyle與AT&TStyle、MASM與NASM機器語言因為電路關係,原始形式就是010101這種二進位形式,但你喜歡也可以轉成十六進位寫出來給別人看。

下面這是一個x86機器語言指令(instruction): 1050A000000(十六進位表示) 用人類說法就是你告訴某顆x86家族的CPU:「把你的eax暫存器內容取出,將其跟10相加,再把結果寫回eax暫存器」 用C語言表示法就是: 1eax+=10; 機器語言形式顯然太麻煩了。

助憶符號於是發明了「助憶符號」,比如用add代表「相加這個運算動作」;減法動作,用符號sub標記;將資料從A處複製過去B處的動作,就用mov助憶符號標記。

add,sub,mov…等是運算子,而eax暫存器跟10是運算參與單元(運算元)。

如果綜合以上講的運算子跟運算元,想要寫出完整指令時,還會有一個問題!若有eax,ecx兩個運算元,想要把eax的值取出,複製到ecx去 到底該寫moveax,ecx還是movecx,eax?哪邊來源?哪邊目的? AT&T、Intel各自有一套語法慣例 C語言 Intel AT&T 指派運算子的左邊是目的地inteax=4; 靠最左邊的運算元是目的地。

moveax,4 靠最右邊的運算元是運算結果放置處。

movl$4,%eax(暫存器名稱前,需加%符號;而且4這個立即數值前,需加$符號;且用movl表示movelong這麼長) 西瓜靠大邊,跟大家一起用Intel慣例的寫法就好。

像上面moveax,4這樣子的指令形式,都叫「組合語言」,說穿了只是把當初的「x86機器語言」寫成比較容易看懂的形式而已。

記憶體模型如果需要更詳細的了解可以參考x86 16位元記憶體模型—Segment:Offset(分段記憶體模型) 32位元記憶體模型—FlatMemoryModel加Paging 個人電腦x86家族的CPU,在16位元時代是8088/8086/80286這三位;而x86家族第一個32位元始祖是劃時代的80386。

8088跟8086的位址匯流排都有20條線,每條線都是一端連接記憶體,一端連接處理器,在高低電位變化下(0、1),總共可有2^20種控制變化。

換言之,依照每個記憶體位址對應一個位元組的慣例,可以定位2^20大小的記憶體位址;80286則進化到24條位址線,定址能力達2^24,即16M記憶體。

省麻煩,把它當成跟8088/8086一樣,只能定址到1M記憶體就好。

在x86的術語中,記憶體位址可以分成三種: 邏輯位址 線性位址 真實位址(物理位址) 必須先「邏輯位址→線性位址」,然後接著才是「線性位址→真實位址」。

自從32位元CPU出現(自80386後),記憶體Model變成FlatMemory,邏輯位址就已經等於線性位址了。

然後是因為有「分頁機制」武力介入,所以需要先透過分頁機制轉換,線性位址才會變成真正的物理位址。

而分頁機制是從80386開始使用(保護模式的完整版也是從80386開始)。

那為什麼16位元處理器,不使用FlatMemoryModel?為什麼當初的邏輯位址要先經過轉換才會變成線性位址? 因為16位元CPU內部,參與運算的暫存器當時都還停留在16位元(如:AX,BX,CX,DX),甚至最重要的指令暫存器(IP)也是16位元,故只有定位到0~65535也就是64K記憶體的能力。

Intel用額外提供的四個「分段暫存器」(CS、DS、ES、SS),搭配其他暫存器後,使得每次記憶體定址方式其實是Segment:Offset,此時這種位址表達法叫邏輯位址。

CS是CodeSegment、DS是DataSegment、SS是StackSegment… 邏輯位址→線性位址,公式是:「SegmentRegister*0x10+Offset」 假設我們有個程式,裡面的「全域變數」(不是放在堆疊的那種區域變數),有個很大的整數陣列,總共有128K。

當程式執行時,這些資料區段假設放在「線性位址=物理位址」的0x0~0x1FFFF這段連續的記憶體空間裡。

在邏輯位址(以寫程式的角度去觀看的位址),這128K資料會被分成兩段,第一段是DS=0且offset=0x0000~0xFFFF,第二段是DS=1且offset=0x0000~0xFFFF。

換言之,如果我要把某陣列元素移到ax暫存器,指令可能長這樣 1movax,wordptr[0x1234] 如果執行這行時DS=0,則會取到物理記憶體位址0x01234處(第一段);如果執行這行時DS=1,則會取道物理記憶體位址1*0x10+0x1234=0x11234。

若要讀取最後一個位元組到ax,只要執行以下指令即可: 12movds,1moval,byteptr[0xFFFF] 因為不知道當時的DS值為多少,所以保險點,先設定DS,然後因為ax是16位元,不必用到這麼大。

所以用al存放即可。

al就是ax暫存器低8位元別名。

(ax在32位元以上的CPU時,其實也是eax暫存器的低16位元處別名) 現今的執行檔,比如PE執行檔,往往內部都有分.text(.code)、.data,可能就是承襲當初的記憶體分段機制? 註:以上參考了[心得]個人的x86組合語言觀念筆記 CheatEngine-第十章|CETutorialStep9 x86組合語言-第二章|x86架構及暫存器解釋



請為這篇文章評分?