程序語言(C,Java等)是人類能夠理解的語言,因此我們可以用程序語言編寫軟體,交給CPU去執行,如果你直接將程序語言交給CPU去執行,CPU壓根就不能理解這種語言,CPU只認識它自己的語言-機器語言即機器碼,因此我們需要將程序語言翻譯成機器碼,翻譯的方式有兩種,一種是編譯,另外一種是解釋,編譯的意思是把整個程序一次性翻譯成機器碼,解釋的意思說,一句一句地翻譯,不管怎麼樣,我們最終交給CPU的是機器碼,讓CPU去根據機器碼去做它該做的事情。
C翻譯成機器碼
可以把計算機當做一個黑盒子,輸入機器碼和數據,CPU執行後輸出內容到內存,如下圖所示
機器碼執行圖
彙編語言是人類能夠理解的機器指令,後續我們舉例子時,直接用彙編語言表示機器指令,它們是等價的。
一.什麼是機器碼?
機器碼也叫機器指令,就像我們人類發出的指令一樣,比如:「小明,去衛生間里拿一把掃帚去打掃房間,機器碼就是這條指令,這條指令里就包含了幹什麼事情,去哪裡干。
1.1 機器碼都有些什麼內容
機器碼是與計算機硬體緊密關聯的,不同的CPU,機器碼不一樣,就好比不同的國家,語言不一樣類似,比如我們常用的CPU如Inter Pentium系列,酷睿系列,AMD 銳龍,ARM,MIPS,IBM PowerPC等,他們的機器碼就各不相同, 但都有一個共同的規範就如下圖所示。
機器碼包含的內容
如上圖所示機器碼共同的規範就是他們都包括操作碼,控制位(可選),操作數(可選),操作數可以有多個(一般1-3個)甚至沒有。
操作碼:表示指令類型,比如ADD表示加法指令,SUB表示減法指令。一個處理器可以包含非常複雜,面面俱到的指令,也可以包含十分精簡,短小精幹的指令,正因如此,在CPU界分成了兩大門派CISC(複雜指令集)和RISC(精簡指令集),CISC屬於大包大攬,啥都想干,RISC是我只敢我能做的事情,其他的事情交給編譯器,下面的表格為這兩個門派的成員
| CISC | RISC |
| Inter,AMD | ARM,MIPS,IBM PowerPC |
控制位:幾乎用不到,不做詳解
操作數:表示指令需要操作的數據,在機器碼中,操作數可不一定是放在那裡,等著你直接去拿的,有以下幾種獲取的方式
立即數定址:立即數是一個整數,例如5,不需要從寄存器或者內存中獲取,它本身就是操作數,例如ARM CPU下的指令
ADD r1,r2,#5上面代碼中5就是立即數,是常量,直接將R2寄存器中的內容與5相加,存儲到r1寄存器中。
直接定址:從內存中直接獲取操作數,例如Inter Pentium CPU下的指令
MOV AX,[2468]上面代碼將內存地址2468上指向的內容寫入到ax寄存器
間接定址:不直接從內存地址獲取操作數,而是將內存地址存儲在寄存器中,通過寄存器間接獲取操作數,例如ARM下的指令
LDR r1, [r2]上面代碼,r2寄存器中存儲的是內存地址,上面指令表達的意思是,將r2寄存器中存儲的內存地址指向的內容寫入到r1寄存器。
偏移量間接定址:它是間接定址變種的一種,例如ARM下的指令
LDR r2,[r3,#8]上面代碼,r3寄存器中存儲的是內存地址,上面指令表達的意思是,將r3寄存器中存儲的內存地址加上偏移8後算出的內存地址指向的內容寫入到r2寄存器。
1.2 機器碼的格式
正如上文提到不同的CPU類型機器碼不同,有的CPU機器碼支持動態長度,動態擴展,如Inter系列,它有時候可以一條指令中有多個操作碼,有的CPU的機器碼長度是固定的,如ARM,我們可以分析大多數的機器碼,大多數的機器碼佔一個字(32位或64位),一般超過一個字的機器碼不多,可能只有Inter系列會有,一般用不到,這裡不做闡述。
大多數的機器碼格式包括以下四種:
三地址機器碼格式
三地址機器碼格式主要在RISC CPU中用到,例如下邊ARM一條彙編指令
ADD r1,r2,r3上述代碼表示將r2寄存器中的值加上r3寄存器中的值,寫入達到r1寄存器
雙地址機器碼格式
雙地址機器碼格式主要在CISC CPU中用到,例如下邊Inter Pentium的一條彙編指令
ADD AX BX上述代碼講過BX寄存器的內容與AX相加後,寫入到AX中,雙地址機器碼的壞處也很明顯了,例如AX寄存器的值執行命令後就被覆蓋了,不能夠重用了。
單地址機器碼格式
單地址機器碼格式在RISC,CISC CPU都會用到,例如下邊ARM一條彙編指令
LDA r1上述代碼表示將r1寄存器的值加到累加器(一種寄存器,用於存儲中間結果),單地址機器碼其實也是有兩個操作數,一個是r1,另一個就是累加器,因為LDA r1已經意味著追加累加器的意思,所有不需要指定。
零地址機器碼格式
零地址機器碼格式一般操作棧頂的數據,不需要寄存器參與,零地址機器碼格式不會用到地址,對於單操作數運算,例如遞增,遞減,取負,清零等一元運算,直接用棧頂的數據,對於雙操作數從棧頂陸續出棧兩個數,然後運算,下邊舉例子,例如下邊的表達式
Z=(A+B).(C-D)
PUSH A ;A入棧
PUSH B ;B入棧
ADD ;棧頂兩個數出棧,相加,然後結果入棧
PUSH C ;C入棧
PUSH D ;D入棧
SUB ;棧頂兩個數據出棧,相減,然後結果入棧
MUL ;棧頂兩個數據出棧,相乘,然後結果入棧
POPZ ;棧頂數據出棧
二.番外篇
2.1 寄存器
寄存器和內存一樣都是存儲器,從功能上講跟內存沒什麼區別,都是用來存儲數據,從其他角度來看,寄存器和內存有如下區別
a.寄存器的速度比內存快N的等級,因此為了加快CPU獲取指令或者數據的速度,就會將最近要訪問的數據放入到寄存器中。
b.寄存器的個數是有限制,一般計算機不到百個,寄存器定址直接用名稱,例如ARM CPU寄存器 r1,r1就是寄存器的名稱,可以通過r1直接訪問寄存器的內容,而內存則用地址,例如1234表示內存的地址
在CPU中寄存器分為以下幾種
內存地址寄存器(程序員不可見)
保存了當前正在讀或者正在寫的內存地址
內存數據寄存器(程序員不可見)
保存了當前剛從內存讀出的數據或者準備要寫入內存的數據
PC 程序計數器(程序員不可見)
保存了下一條要執行的指令的內存地址
IR指令寄存器(程序員不可見)
保存了當前正在執行的指令
通用寄存器(程序員可見)
存儲臨時的數據例如中間結果)
特殊寄存器(程序員不可見)
例如累加器,條件寄存器等
2.2 機器碼長度設計
在1.2章節中闡述了機器碼的格式,沒有指出機器碼長度的設計,這裡指出。
指令長度演算法:
操作碼長度+控制位長度+寄存器定址個數*寄存器佔用位數+內存直接取址個數*內存佔用位數+立即數長度
操作碼長度:
CPU支持的指令個數,如果支持8個指令,那操作碼長度就是3
控制位長度:
這個是固定的
寄存器定址個數*寄存器佔用位數:
寄存器佔用位數可以通過程序員可以訪問的寄存器個數換算得知,如果寄存器可以訪問的寄存器個數為32,那麼寄存器佔用位數就是5(2的5次方=32)
寄存器定址個數表示機器碼中通過寄存器獲取操作數或者間接通過寄存器獲取操作數的寄存器個數例如
ADD r1,r2 表示通過r1,r2兩個寄存器獲取操作數,那麼寄存器定址個數就是2
內存直接取址個數*內存佔用位數:
內存直接取址個數表示有多少通過內存直接定址獲取操作數,例如
MOV BX,[1234] 表示從1234內存地址獲取操作數,內存直接取址個數為1
立即數長度
根據立即數的範圍得出,例如立即數範圍為0-999,那麼立即數長度就是10,2的10次方=1024剛好大於999,可以完全包含立即數的範圍。
舉例論證:
以三地址機器碼格式為例,假設目的地址,源地址都是寄存器,那麼一個寄存器佔用5位,3個寄存器佔用15位,對於32位字長的cpu,操作碼+控制位就是17位,如果操作數部分不夠15位,則操作數部分自動擴展為32位。
三地址機器碼格式
原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/228165.html
微信掃一掃
支付寶掃一掃