
《計算機是如何工作的人人都能懂的計算機軟硬件工作原理》第 8 章 機器碼與匯編語言 閱讀筆記 8我們已經(jīng)介紹了計算機的物理部分內存、CPU和 I/O 設備。理解計算機的硬件部分很重要但是硬件只起一半作用。計算機的魔力在于軟件。軟件把計算機從固定用途的設備變成了高度靈活的設備它能輕松地實現(xiàn)新的功能本章將介紹底層軟件——機器碼和匯編語言。我發(fā)現(xiàn)交互方法是理解這些主題的最好方法所以本章的大部分內容都體現(xiàn)在設計中。8.1 軟件術語要討論軟件需要首先介紹一些術語。告訴計算機做什么的指令被稱為 “軟件”?這與硬件形成了對比硬件是計算機的物理元素。完成任務的一組有序的軟件指令被稱為 “程序”?編程就是編寫這種程序的行為。術語 “應用程序” 有時被用作程序的同義詞盡管應用程序往往指的是直接與人進行交互的程序而不是與軟件或硬件交互的程序。應用程序也可能由多個程序構成。app 這個詞大約在 2008 年開始流行其中含有的其他意思將在第 13 章中介紹。另一個指代一組軟件指令的名稱是 “計算機代碼” 或 “代碼”?。CPU 執(zhí)行 “機器碼”?而軟件開發(fā)人員一般用高級編程語言編寫源代碼。術語 “源代碼” 是指由開發(fā)人員最初編寫的程序文本。這種代碼一般不會用 CPU 能直接理解的形式來編寫因此在其能被計算機運行之前還需要進行其他處理。我將在第 9 章詳細介紹源代碼和高級編程語言但是現(xiàn)在我們先來看看軟件的基礎機器碼。機器碼是二進制機器語言指令形式的軟件。如第 7 章所述CPU 的架構決定了 CPU 能理解的指令。就像人類語言是用詞匯表構成的一樣機器語言是用 CPU 系列已知的指令列表構成的。詞匯表中的詞語排列成句子來表達含義CPU 指令排列成程序后也具有相同功能。不管程序最初是如何編寫的編寫程序的方法有很多種?它最終都需要在 CPU 上作為一系列機器語言指令來執(zhí)行。正如你所期望的CPU 指令可以歸結為一系列 0 和 1就像計算機處理的所有其他事情一樣。這里值得重復一下不論程序最初是如何編寫的也不論用的是哪種編程語言更不論使用的是什么技術最后程序都會變成一系列的 0 和 1表示 CPU 可以執(zhí)行的指令。幾年前我有一項工作是關于診斷軟件故障的。通常我要分析發(fā)生在其他公司編寫的軟件中的問題。我沒有這類軟件的源代碼也沒有很多關于它應如何工作的相關信息但是我的工作就是確定軟件故障的原因我有個同事對此很淡然他常常提醒我 “這就是個代碼”?。換句話說故障軟件就是一組被 CPU 解釋為指令的 1 和 0。如果 CPU 可以理解代碼那么你也可以。8.2 機器指令示例我認為切入機器碼主題的最簡單方法就是看示例。我們來看看 ARM 系列處理器能理解的特定機器指令。你可能還記得大多數(shù)智能手機上都能發(fā)現(xiàn) ARM 處理器所以你的手機可能也可以理解這條指令。我們的示例指令告訴處理器把數(shù)字 4 移動到寄存器 r7 中r7 是 ARM 處理器通用寄存器中的一個?;貞浺幌挛覀冎坝懻摰挠嬎銠C硬件寄存器是 CPU 中的一種小容量存儲位置。執(zhí)行這個操作的 ARM 指令的二進制形式如下我們來看看 ARM CPU 是如何理解這條指令的如圖 8-1 所示。注意我們跳過了無關的一些位。圖 8-1 ARM 指令條件部分指示了在什么條件下該指令應該執(zhí)行。1110 意味著這條指令是無條件執(zhí)行的所以 CPU 應該總是執(zhí)行它。雖然本例中不是這樣的但是有些指令只需要在特定條件下執(zhí)行。接下來的兩位本例中為 00與我們的討論無關所以跳過它們。立即數(shù)位告訴我們將要訪問的是寄存器中的值還是指令本身指定的值稱為立即數(shù)?。本例中立即數(shù)位為 1要訪問的是指令中指定的數(shù)。如果立即數(shù)位是 0那么被訪問寄存器將由指令中的其他位來指定。操作碼表示 CPU 要執(zhí)行的操作。本例中操作碼是 mov意思是 CPU 要移動數(shù)據(jù)。目標寄存器 0111 告訴我們把值移動到寄存器 r70111 是十進制的 7中。最后立即數(shù)值 00000100 是十進制的 4這是我們想要移動到 r7 中的數(shù)。簡單概括一下這個二進制序列告訴 ARM CPU 把數(shù)字 4 移動到 r7 寄存器中。CPU 總是用二進制處理各種事情但是絕大多數(shù)人很難處理這些 0 和 1。現(xiàn)在我們用十六進制表示同樣的指令以便讓它更容易讀懂現(xiàn)在是不是好點啦好吧也許不是。這種格式比二進制格式更加緊湊也更容易區(qū)分但它的含義仍然不明顯。對我們而言幸運的是還有另一種方法來表示這條指令匯編語言。匯編語言是一種編程語言它的每一條語句都直接表示一條機器語言指令。每種類型的機器語言都有對應的匯編語言—如 x86 匯編語言、ARM 匯編語言等。匯編語言語句由一個表示 CPU 操作碼的助記符和其他必需的操作碼比如寄存器或數(shù)值組成。助記符是人類可讀的操作碼形式它允許匯編語言程序員在代碼中用 mov 而不是 1101。之前討論過的相同的 ARM 指令也可以用如下匯編語言語句表示與相應的二進制和十六進制表示相比這個語句當然是表示 “把 4 移動到 r7 寄存器中的” 更好的方式最起碼對人類來說更容易閱讀。也就是說匯編語言語句只是為了人類方便。CPU 從不會執(zhí)行文本格式的指令它只處理二進制格式的指令。如果程序員用匯編語言編寫程序那么在計算機執(zhí)行該程序之前匯編指令仍然必須轉換成機器碼。這要用 “匯編器” 來完成匯編器是一種把匯編語言語句轉換成機器碼的程序。匯編語言文本文件被送入?yún)R編器匯編器輸出的就是包含機器碼的二進制目標文件如圖 8-2 所示。圖 8-2 匯編器把匯編語言轉換成機器碼8.3 用機器碼計算階乘現(xiàn)在我們已經(jīng)查看了單條 ARM 指令接下來我們看看如何把多條指令組合在一起來執(zhí)行有用的任務。我們來看一個計算整數(shù)階乘的 ARM 機器碼。你可能還記得在數(shù)學課上學過n 的階乘寫作 n!是小于或等于 n 的正整數(shù)的乘積。例如4 的階乘為4!4×3×2×124現(xiàn)在我們已經(jīng)有了階乘的定義讓我們來看看用 ARM 機器碼如何實現(xiàn)階乘算法。為了簡單起見我們不查看完整的程序代碼只看執(zhí)行階乘算法的部分。我們假設 n 的初始值保存在寄存器 r0 中并且當代碼完成時計算結果也保存在 r0 中。如同計算機處理的任何其他數(shù)據(jù)一樣在 CPU 訪問機器碼之前必須把它加載到內存中。下面的是機器碼的 32 位4 字節(jié)十六進制表示以及每個值的內存地址。當我們的代碼加載到內存時階乘邏輯從地址 0001007c 開始。來看看從這個地址開始的內存內容。請注意0001007c 不是一個神奇的地址它只是恰好是本例中代碼加載的地方。還要注意內存地址值增幅為 4因為每個數(shù)據(jù)值都需要 4 個字節(jié)來存放。每條 ARM 指令長度都為 4 字節(jié)所以這里的數(shù)據(jù)表示 5 條 ARM 指令。把這些指令看作十六進制的值并不能讓我們深入理解它們的含義所以讓我們對這些指令進行譯碼以便理解這個程序。在下面的清單中我已經(jīng)把十六進制的數(shù)值轉換成相應的匯編語言助記符。如果你好奇的話把機器語言手動翻譯成匯編語言不是你需要了解的我們有完成這項功能的軟件稱為 “反匯編器”??,F(xiàn)在這本書就充當了你的反匯編器。下面列出了每條指令以及與之配對的匯編語句CPU 按順序執(zhí)行這些指令直到遇到分支指令比如 ble 或 bne?分支指令會讓它跳轉到程序的另一個部分。地址 00010090 標記著階乘邏輯的結束。一旦到達這個地址階乘運算結果就已經(jīng)保存到 r0 了。此時CPU 執(zhí)行位于地址 00010090 處的指令。你可能好奇這些指令是如何表示階乘的計算的。對大多數(shù)人來說粗略地看一下這些指令不足以理解其所含的意思。采取循序漸進的方式在每條指令執(zhí)行時跟蹤寄存器的值能幫助你理解這個程序。我將給你提供一些必需的背景資料然后你可以嘗試評估這個程序是如何工作的。為了理解這個程序你首先需要對每條指令的說明。在表 8-1 中我已經(jīng)給出了這個程序中每條指令的解釋。在這個表中我使用了寄存器的占位符名稱比如 Rd 和 Rn。當你查看匯編代碼時你將看到實際使用的寄存器名稱比如 r0 和 r3。代碼中列出的操作數(shù)順序對應于表 8-1 中的操作數(shù)順序。例如subs r3r0#1 表示從存儲在 r0 中的值中減去 1并把結果存放到 r3 中。表 8-1 一些 ARM 指令的解釋分支和狀態(tài)寄存器分支指令實際并不查看前一條指令的數(shù)值結果。和大多數(shù) CPU 一樣ARM 處理器也有一個寄存器專門跟蹤狀態(tài)。這個狀態(tài)寄存器有 32 位每個位對應一個特定的狀態(tài)標志。例如位 31 是 N 標志當指令結果為負數(shù)時該標志置 1。只有某些指令會影響這些標志狀態(tài)。例如subs 指令會改變標志的狀態(tài)。如果某個減法運算結果是負的那么 N 標志會置 1否則它會清零。其他指令包括分支指令則查看狀態(tài)標志位來決定該做什么。這看上去是一種迂回的方式但實際上它簡化了像 bne 這樣的指令——處理器可以根據(jù)單個位的值進行分支或不進行分支?。8.4 總結本章討論了機器碼即特定于 CPU 的一系列指令它們在內存中以字節(jié)表示。你學習了如何對示例 ARM 處理器指令進行編碼以及如何用匯編語言表示該指令。你了解到匯編語言是一種源代碼尤其是人類可讀形式的機器碼。我們還了解了怎樣組合多條匯編語言語句來執(zhí)行有用的操作。第 9 章將討論高級編程語言。這種語言提供了CPU 指令集的抽象允許開發(fā)人員編寫更容易理解且可以在不同計算機硬件平臺之間移植的源代碼。