1. 程式人生 > >編碼的奧祕:兩種典型的微處理器

編碼的奧祕:兩種典型的微處理器

轉自:《編碼的奧祕》    第十九章

 

            微處理器—整合計算機中央處理器( C P U)的所有元件在一個矽晶片上—誕生於1 9 7 1年。它的產生有一個很好的開端:第一個微處理器是Intel 4004,其中有2 3 0 0個電晶體。今天,差不多3 0年過去了,為家用計算機所製造的微處理器中將近有10 000 000個電晶體。

            微處理器實際的作用基本上保持不變。現在的晶片上附加的上百萬個電晶體可以做許多有趣的事情,但在微處理器最初的探索過程中,這些事情更多的是分散我們的注意力而不是給我們以啟迪。為了對微處理器的工作情況獲得更清晰的認識,讓我們先看一下最初的微處理器。

           這些微處理器出現在1 9 7 4年。在該年度, I n t e l公司在4月推出了8 0 8 0,M o t o r o l a(摩托羅拉)—從2 0世紀5 0年代開始生產半導體和電晶體產品的公司—在8月份推出了6 8 0 0。它們並非是那年僅有的微處理器。同樣是在1 9 7 4年,德克薩斯儀器公司推出了4位的TMS 1000,用在許多計算器、玩具和裝置上; National Semiconductor(國家半導體公司)推出了PA C E,它
是第一個1 6位的微處理器。然而,回想起來, 8 0 8 0和6 8 0 0是兩個最具有重大歷史意義的晶片。

            I n t e l設定8 0 8 0最初價格為$ 360,這是對IBM System/360的一個諷刺。IBM System/360是一個大型機系統,由許多大公司使用,要花費幾百萬美元。(今天,你只花$ 1 . 9 5就可以買到一個8 0 8 0晶片。)這並不是說8 0 8 0可以與S y s t e m / 3 6 0相提並論,但不用幾年, I B M公司也將會正視這些很小的計算機。

           8 0 8 0是一個8位的微處理器,有6 0 0 0個電晶體,時鐘頻率為2 M H z,可定址6 4 K B的儲存空間。6 8 0 0(今天也只賣$ 1 . 9 5)有大約4 0 0 0個電晶體,也可定址6 4 K B的儲存空間。第1代6 8 0 0的時鐘頻率為1 MHz,但到1 9 7 7年M o t o r o l a公司釋出新款的6 8 0 0時,其時鐘頻率已為1 . 5 M H z和2 MHz。

           這些晶片稱作“單晶片微處理器”,不太準確的名稱為“一個晶片上的計算機”。處理器只是整個計算機的一部分。此外,計算機至少還需要一些隨機訪問儲存器( R A M)、供人們輸入資訊到計算機的方法(輸入裝置),供人們從計算機獲取資訊的方法(輸出裝置),以及其他可把所有這些東西連線在一起的晶片。這些元件將在第2 1章詳細介紹。

           從現在起,我們只考察微處理器。描述微處理器時,通常是用框圖來畫微處理器的內部元件及它們是如何連線的。但在第1 7章已有夠多的圖了,現在,我們將通過觀察處理器與外界的相互作用來了解它的內部。換句話說,為了弄清楚它的作用,可以把微處理器看成是一個黑盒子,它的內部操作不需要做詳細研究。我們可以通過測試晶片的輸入和輸出訊號,特別是晶片的指令集來掌握微處理器的功能。 

             8 0 8 0和6 8 0 0都是4 0管腳的積體電路。這些晶片最普通的I C封裝大約是2英寸長,半英寸寬,1 / 8英寸厚:

             當然,你看到的只是外包裝。位於其內部的矽晶片非常小,就拿早期的8位微處理器來說,其矽晶片小於1 / 4平方英寸。外包裝保護矽晶片並通過管腳提供對晶片的輸入和輸出點的訪問。下圖顯示了8 0 8 0的4 0個管腳的功能:

             本書的所有電氣或電子裝置都需要某種電源供電。8 0 8 0的一個特別之處在於它需要三種電源電壓:管腳2 0必須連到5伏電源上,管腳11連到- 5伏電源上,管腳2 8連到1 2伏電源上;管腳2接地(1 9 7 6年,I n t e l釋出了8 0 8 5晶片,簡化了這些電源需求)

             其餘管腳都畫有箭頭。從晶片中出來的箭頭表示輸出訊號,這是由微處理器控制的訊號,計算機中其餘晶片對此作出響應。指向晶片的箭頭表示輸入訊號,這是來自於其他晶片的訊號,8 0 8 0對它們做出響應。有些管腳既是輸入又是輸出。

             第1 7章的處理器需要振盪器使它工作。8 0 8 0需要兩個不同的2 MHz同步時鐘輸入,在2 2和1 5管腳上分別標記為。這些訊號可以很方便地由I n t e l公司生產的8 2 2 4時鐘訊號發生器提供。給這個晶片連上一個18 MHz的石英晶體,剩下的工作它基本上可以完成。

             一個微處理器通常有多個輸出訊號來定址儲存空間,這種訊號的數目與微處理器可定址的儲存器空間的大小直接相關。8 0 8 0有1 6個地址訊號,標為A 0~A 1 5,具有定址即65 536位元組的儲存空間的能力。

            8 0 8 0是一個8位微處理器,一次可從儲存器中讀出、寫入8位資料。它包括8個數據信號D0~ D7,這些訊號是在此晶片中僅有的幾個既作為輸入又作為輸出的訊號。當微處理器從儲存器讀資料時,這些管腳作為輸入;當微處理器向儲存器寫資料時,這些管腳作為輸出。

           微處理器的另外1 0個管腳是控制訊號。例如, R E S E T輸入用來複位微處理器。輸出訊號-W R表示微處理器要向R A M中寫資料。(-WR訊號對應於R A M陣列的寫入輸入。)另外,當晶片讀取指令時,其他控制訊號會在某個時候出現在D0~ D7管腳。由8 0 8 0構成的計算機系統通常使用8 2 2 8系統控制晶片來鎖存這些附加的控制訊號。後面將會講述一些控制訊號。由於8 0 8 0的控制訊號非常複雜,因此,除非你想基於8 0 8 0晶片來實際設計計算機,否則最好不要用這些控制訊號來折磨自己。

           假定8 0 8 0微處理器連線了6 4 K B的儲存器,這樣可以不通過微處理器來讀寫資料。

           8 0 8 0晶片復位後,它從儲存器的地址0 0 0 0 h處讀取該位元組,送到微處理器中。這可以通過在地址訊號端A0~A1 5輸出1 6個0來實現。它讀取的位元組必須是8 0 8 0指令,這種讀取位元組的過程叫作取指令。

           在第1 7章構造的計算機裡,所有指令(除了停止指令H LT)都是3個位元組長,包括一個操作碼和兩個位元組的地址。在8 0 8 0中,指令長度可以是1個位元組、2個位元組或3個位元組。有些指令可使8 0 8 0從儲存器的某一位置處讀出一個位元組送到微處理器中;有些指令可使8 0 8 0從微處理器中把資料寫入儲存器的某一位置處;其他指令可使8 0 8 0不使用R A M而在內部執行。第一條
指令執行完後, 8 0 8 0訪問儲存器中的第二條指令,依此類推。這些指令組合在一起構成一個計算機程式,用來完成一些自己感興趣的事情。

          當8 0 8 0執行在最高速度即2 MHz時,每個時鐘週期為5 0 0納秒( 1除以2 000 000周等於0 . 0 0 0 0 0 0 5 0 0秒)。第1 7章中的每條指令都需要4個時鐘週期, 8 0 8 0的每條指令則需要4~1 8個時鐘週期,這意味著每條指令的執行時間為2~9微秒(即百萬分之一秒)。

         瞭解微處理器功能的最好方法可能是在系統方式下測試其完整的指令集。

         第1 7章最後出現的計算機僅有1 2條指令。一個8位微處理器很容易就有2 5 6條指令,每個操作碼對應於某個8位值。(如果一些指令有2個位元組的操作碼,則實際會有更多的指令)。8 0 8 0雖沒有那麼多,但它也有2 4 4條操作碼。這看起來似乎很多,但總的來說,卻又不比第1 7章中的計算機功能多多少。例如,如果想用8 0 8 0做乘法或除法,仍然需要寫一段小程式來實現。

         第1 7章中講過,處理器指令集的每個操作碼都和某個助記符相聯絡,有些助記符之後可能還有運算元。但這些助記符僅用來方便地表示操作碼。處理器只讀取位元組,它並不懂組成這些助記符的字元的含義。

         第1 7章中的計算機有兩條很重要的指令,稱作裝載( L o a d)和儲存(S t o r e)指令。這些指令都佔用三個位元組的儲存空間。裝載指令的第一個位元組是操作碼,操作碼後的兩個位元組表示1 6位地址。處理器把在此地址中的位元組送到累加器。同樣,儲存指令把累加器中的內容儲存到指令指定的地址處。

         下面,我們用助記符來簡寫這兩個操作:

           在此,A表示累加器(裝載指令的目的運算元,儲存指令的源運算元),a a a a表示一個1 6位的儲存器地址,通常用4位十六進位制數來表示。 

          8 0 8 0中的8位累加器稱作A,就像第1 7章中的累加器。正如第1 7章中的計算機一樣, 8 0 8 0也有兩條與裝載和儲存指令功能一樣的指令。8 0 8 0中這兩條指令的操作碼為3 2 h和3 A h,每個操作碼後有一個1 6位地址。8 0 8 0的助記符為S TA(代表儲存累加器的內容)和L D A(代表裝載到累加器):

             除了累加器,8 0 8 0微處理器內部還包括6個暫存器( r e g i s t e r),每個暫存器可以儲存8位的值。這些暫存器和累加器非常相似,事實上,累加器被看作是一種特殊的暫存器。和累加器一樣,這6個暫存器也是鎖存器。處理器可以把資料從儲存器傳送到暫存器,也可以把資料從暫存器送回到儲存器。然而,這些暫存器沒有累加器的功能強大。例如,當兩數相加時,其
結果通常送往累加器而非其中一個暫存器。

            在8 0 8 0中,除累加器外的6個暫存器的名字分別為B,C,D,E,H和L。人們通常問的第一個問題是“用F和G幹什麼?”,第二個問題是“用I,J和K又要做什麼?”,答案是使用暫存器H和L具有某種特殊的含義。H代表高(H i g h),L代表低( L o w )。通常把H和L的8位合起來記作H L來表示一個1 6位暫存器對,H作為高位位元組,L作為低位位元組。這個1 6位值通常用來尋
址儲存器。後面我們將看到它的簡單用法。

             所有這些暫存器都是必需的嗎?為什麼不在第1 7章中的計算機中用到它們呢?從理論上說,它們並非必需,但是使用它們會很方便。許多計算機程式在同一時刻要用到幾個資料,如果所有這些資料都儲存在微處理器的暫存器中而非儲存器中,執行程式將會更快,因為程式訪問儲存器的次數越少,那麼它的執行速度也就越快。

             8 0 8 8指令中,有一個至少6 3個指令碼供一條8 0 8 0指令使用的指令,它就是M O V指令,即M o v e的簡寫。該條指令只有一個位元組,用於把一個暫存器中的內容傳送到另一個暫存器中(或同一個暫存器中)。使用大量M O V指令是設計帶有7個暫存器(包括累加器)的微處理器的正常結果。

             下面是前3 2條M O V指令。記住目的運算元在左邊,源運算元在右邊:

            這些都是很方便的指令。當一個暫存器中有值時,可以把它傳送到其他暫存器中。注意,上述指令中有四條指令用到H L暫存器對,如:

           

            前面列出的L D A指令把一個位元組從儲存器中傳送到累加器中,這個位元組的1 6位地址直接跟在L D A操作碼的後面。這裡的M O V指令把一個位元組從儲存器中傳送到暫存器B中,但被裝載到暫存器中的位元組的地址是儲存在暫存器對H L中。H L暫存器是怎樣得到1 6位儲存器地址的呢?它可以通過多種方法來實現,或許是通過某種方法計算出來的。

            總之,這兩條指令

           都把一個位元組從儲存器中裝載到微處理器中,但它們用兩種不同的方法來定址儲存器地址。第一種方法叫作直接定址方式,第二種方法叫作間接定址方式。

            第二批3 2條M O V指令表明用H L定址的儲存器地址也可以作為目的運算元:

            其中一些指令如:

           做的是無用的事,而像:

           這樣的指令是不存在的。和這條指令相對應的操作碼實際上是停止指令。

           觀察這些M O V操作碼更明顯的方法是考察它的位模式, M O V操作碼由8位組成:

            其中字母ddd 代表指代目的運算元的3位程式碼,s s s代表指代源運算元的3位程式碼。這3位程式碼是:

           例如,指令:

           相應的操作碼錶示為0 11 0 1 0 11,或6 B h。可以通過檢查前面的表來驗證。 

           因此可能在8 0 8 0內部某個地方,標有s s s的3位標識用在8 - 1資料選擇器中,標有d d d的3位標識用於控制3 - 8譯碼器,此譯碼器用來決定哪一個暫存器鎖存了一個值。

           也可能使用暫存器B和C來構成一個1 6位暫存器對B C,用暫存器D和E來構成一個1 6位暫存器對D E。如果每一個暫存器對都包含用於裝載或儲存一個位元組的儲存器地址,則可以使用下述指令:

            另一種型別的傳送指令叫做傳送立即數,指定的助記符為M V I。傳送立即數指令佔兩個位元組,第一個是操作碼,第二個是1個位元組的資料。此位元組從儲存器中傳送到一個暫存器中或由H L定址的儲存單元中。

            例如,當指令:

             執行後,暫存器E中包含有位元組3 7 h。這就是第三種定址方式,即立即數定址方式。

             3 2個操作碼的集合完成四種基本算術運算,那是在第1 7章開發處理器時我們就已熟悉的運算,即加法( A D D)、進位加法( A D C)、減法( S U B)和借位減法( S B B)。所有情況中,累加器是兩個運算元之一,也是結果的目的地址。

            假設A中是3 5 h ,暫存器B中是2 2 h ,當指令:

            執行後,累加器中的結果為1 3 h。

            若A中的值為3 5 h ,暫存器H中的值為1 0 h,L中的值為7 C h,儲存器地址1 0 7 C h中的值為4 Ah,則指令:

            把累加器中的內容( 3 5 h)和通過暫存器對H L定址得到的值( 4 A h)相加,並把結果(7 F h)儲存到累加器中。

            A D C和S B B指令允許8 0 8 0加/減1 6位、2 4位、3 2位和更多位的數。例如,假設暫存器對B C和D E都包含1 6位數,你想把它們相加,並把結果存到B C中。下面是具體做法:

           其中有兩條加法指令, A D D指令用於低位位元組相加, A D C指令用於高位位元組相加。第一條加法指令的進位位包含在第二條加法指令中。因為只能利用累加器進行加法運算,所以在這麼短的程式碼中也需要至少4條M O V指令。許多M O V指令常常出現在8 0 8 0程式碼中。

           該是談論8 0 8 0標誌位的時候了。在第1 7章的處理器中,已有進位標誌位C F和零標誌位Z F。8 0 8 0還有3個標誌位,即符號標誌位S F、奇偶標誌位P F和輔助進位標誌位A F。所有標誌位都儲存在另一個叫作程式狀態字( P S W:program status word)的8位暫存器中。像L D A、S TA和M O V這樣的指令不影響標誌位,而A D D、S U B、A D C和S B B指令卻要影響標誌位,影響的方式如下:

           • 當運算結果最高位為1時,符號標誌位S F為1,表示結果為負。
           • 當結果為0時,零標誌位Z F為1。
           • 當運算結果中“ 1”的個數為偶數時,奇偶標誌位P F = 1;當運算結果中“ 1”的個數為奇數時,奇偶標誌位P F = 0。P F有時               用來粗略地檢測錯誤,此標誌位在8 0 8 0程式中不常用。
           • 當A D D或A D C運算產生進位或S U B與S B B運算不發生借位時,進位標誌位C F = 1。(這點不同於第1 7章中的計算                 機進位標誌的實現。)
           • 當操作結果的低4位向高4位有進位時,輔助進位標誌位A F = 1。此標誌位只用在D A A(十進位制調整累加器)指令中。

           有兩條指令直接影響進位標誌位C F:

           第1 7章中的計算機可執行A D D、A D C、S U B和S B B指令(儘管沒什麼靈活性),但8 0 8 0還可以進行邏輯運算A N D(與)、O R(或)和X O R(異或)。算術運算和邏輯運算都可通過處理器的算術邏輯單元( A L U)來執行:

             A N D、X O R和O R指令按位運算,即邏輯操作只是單獨地在對應位之間進行。例如:

            累加器中的結果將為0 5 h。如果第三條指令為O R運算,則結果為5 F h;如果第三條指令為X O R運算,則結果為5 A h。

            C M P(比較)指令與S U B指令基本上一樣,除了結果不儲存在累加器中。換句話說,C M P執行減法操作再把結果扔掉。這是為什麼?是因為標誌位。根據標誌位的狀態可知道所比較的兩數之間的關係。例如,當如下指令: 

             執行完時,A中的內容沒有改變。然而,若A中的值為2 5 h,則Z F標誌置位;若A中的值小於2 5 h,則CF = 1。 

              這8個算術邏輯運算也可以對立即數進行操作:              例如,上面列出的兩條指令也可以用下面的指令來替換:

               下面是其他兩條8 0 8 0指令:

              C M A即complement accumulator,它對累加器中的值進行取反操作。每個0變為1,每個1變為0。如果累加器中的值為0 11 0 0 1 0 1,C M A指令使它變為1 0 0 11 0 1 0。也可以用下述指令來使累加器按位取反: 

                

              D A A即Decimal Adjust Accumulator,如前所述,它可能是8 0 8 0中最複雜的一條指令。微處理器中有一個完整的小部件專門用於執行這條指令。

             D A A指令幫助程式設計師用B C D碼錶示的數來進行十進位制算術運算。在B C D碼中,每一小塊資料的範圍在0 0 0 0~1 0 0 1之間,對應於十進位制的0~9。利用B C D碼格式,每8位位元組可儲存兩個十進位制數字。

              假設累加器中的值為B C D碼的2 7 h。由於是B C D碼,則它實際上指的是十進位制的2 7。(十六進位制的2 7 h等於十進位制的3 9。)再假定暫存器B中的值為B C D碼的9 4 h。如果執行指令:

              

               累加器中的值將為BB h,當然,它不是B C D碼,因為B C D碼中的每一塊不能超過9。但是,現在執行指令:

               則累加器中的值為2 1 h,且CF = 1,這是因為2 7和9 4的十進位制和為1 2 1。如果想進行B C D碼的算術運算,這樣做是相當方便的。

                經常需要對一個數進行加1或減1操作。在第1 7章的乘法程式中,我們需要對一個數減1,使用的方法是加上FFh ,它是- 1的2的補碼。8 0 8 0中包含特殊的用於暫存器或儲存單元的加1指令(稱作增量)和減1指令(稱作減量):

               單位元組指令I N R和D C R可影響除C F外的所有標誌位。

               8 0 8 0也包含4個迴圈移位指令,這些指令可使累加器中的內容左移或右移1位:

              這些指令隻影響C F。

             假定累加器中的值為A 7 h,即二進位制的1 0 1 0 0 111。R L C指令使A中的內容向左移位,最高位(移出頂端)成為最低位(移進底端),同時決定進位標誌位C F的狀態。其結果為0 1 0 0 1111且CF = 1。R R C指令用同樣的方法向右移位。開始為1 0 1 0 0 111,執行R R C指令後,其結果為11 0 1 0 0 11且CF = 1。 

             R A L和R A R指令有些不同。當向左移位時, R A L指令把C F移入累加器的最低位,而把最高位移入C F中。例如,如果累加器的內容為1 0 1 0 0 111,CF = 0,R A L指令執行的結果是累加器的內容變為0 1 0 0 111 0,且CF = 1。同樣,在相同的初始條件下, R A R指令使累加器的內容變為0 1 0 1 0 0 11,CF = 1。

             對於乘2(左移1位)和除2(右移一位)操作,移位指令非常方便。

             把微處理器定址的儲存器叫作隨機訪問儲存器( R A M)是有原因的:微處理器可以簡單地根據提供的地址訪問某一儲存位置。R A M就像一本書一樣,我們可以開啟它的任何一頁。它並不像做在微縮膠片上的一個星期的報紙,要找到週六版,需掃過大半周。同樣,它也不同於磁帶,要播放磁帶上的最後一首歌需快進整個一面。微縮膠片和磁帶的儲存不是隨機訪問的,而是順序訪問的。

             R A M確實效果不錯,對於微處理器來說更是如此。但在使用儲存器時有所差別是有好處的,下面就是一種既非隨機又非順序訪問的儲存方式:假定你在一個辦公室裡,人們到你桌前給你分配工作,每個工作都需要某種資料夾。通常你會發現你在繼續某項工作之前,必須使用另外一個資料夾先做一些相關的工作。因此你把第一個資料夾放在桌子上,又拿出第二個資料夾放在它上面進行工作。現在又有一個人來讓你做一個優先權高於前面工作的工作,你拿來一個新資料夾放在那兩個上面繼續工作。而此項工作又需要另外一個資料夾,這樣在你的桌子上很快就擺了一堆檔案夾了。

             注意,這個堆非常明確地、有序地儲存了你正在做的工作的軌跡。最上面的資料夾總是最高優先權的工作,去掉這個以後,下一個肯定是你就要做的,如此類推。當你最終去掉了桌子上的最後一個資料夾後(你開始的第1項工作),你就可以回家了。

             以這種方式工作的儲存器技術叫做作堆疊( s t a c k)。從底向上壓入堆疊,從頂向下彈出堆疊,因此這也叫後進先出儲存器,或L I F O。最後放入堆疊中的資料最先被取出,最先放入堆疊中的資料最後被取出。 

             計算機中也可以使用堆疊,不是用來儲存工作而是用來儲存資料,且已被證明使用起來非常方便。向堆疊中放入資料叫作p u s h(壓入),從堆疊中取走資料叫作p o p(彈出)。

             假定你正在用匯編語言設計程式,程式中使用了暫存器A、B和C。但在程式設計過程中,你發現此程式需要去做另一件事—一個小的計算,其中也要使用暫存器A,、B、C。而你最終要回到先前的程式,並使用A、B、C中原有的值。

             當然,你能做的工作只是簡單地把暫存器A、B、C中的值儲存到儲存器中的不同位置,以後再把這些位置的值裝載到暫存器中,但這樣做需要儲存值被儲存的位置。一個顯然的方法是把暫存器壓入堆疊:

             一會兒再解釋這些指令的作用。現在,我們只需要知道它們以某種方式把暫存器的內容儲存在一個後進先出的儲存器中。一旦這些語句執行了,你的程式就可以毫無顧慮地利用這些暫存器來做其他工作。為了得到原來的值,只需簡單地按與壓入堆疊相反的順序把它們從堆疊中彈出即可,如下所示:

              記住是後進先出。如果用錯了P O P語句的順序,就會引起錯誤。

              堆疊機制的一個好處在於一個程式的不同部分都可以使用堆疊而不會出現問題。例如,在把A、B、C壓入堆疊中後,程式的其他部分還可能需要把暫存器C、D、E的內容壓入堆疊:

             接著,這一部分程式所要做的就是在第一部分彈出C、B和A之前,用下述方法恢復暫存器的值:

            堆疊是怎樣實現的呢?首先,堆疊只是不被別的東西使用的正常的R A M的一部分。8 0 8 0微處理器包含一個特殊的1 6位暫存器來對這一部分儲存器進行定址,這個1 6位暫存器叫作堆疊指標。 

            這裡舉的壓入和彈出暫存器的例子對於8 0 8 0來說不太準確。8 0 8 0的P U S H指令實際上是儲存1 6位的值到堆疊, P O P指令用來恢復它們。因此8 0 8 0不用像PUSH C和POP C這樣的指令,它有下述8條指令:

             PUSH BC指令把暫存器B和C的內容儲存到堆疊中, POP BC指令恢復它們。最後一行的縮寫P S W指的是程式狀態字,前面講過,它是包含有標誌位的8位暫存器。最後一行的兩條指令實際上是把累加器和P S W都壓入和彈出堆疊。如果你想儲存所有暫存器和標誌位的內容,可以使用: 

            當以後想恢復這些暫存器的內容時,按相反的順序使用P O P指令:

            堆疊是怎樣工作的呢?假設堆疊指標為8 0 0 0 h,PUSH BC指令將引起下面這些情況發生:

            • 堆疊指標減1至7 F F F H
            • 暫存器B的內容儲存在堆疊指標所指的地址處,即7 F F F h處

            • 堆疊指標減1至7 F F E H
            • 暫存器C的內容儲存在堆疊指標所指的地址處,即7 F F E h處。當堆疊指標仍然為7 F F E h時,POP BC指令執行,用來                反向執行每一步:
            • 從堆疊指標所指的地址(即7 F F E h)處裝載資料到暫存器C中
            • 堆疊指標增1至7 F F F h
            • 從堆疊指標所指的地址(即7 F F F h)處裝載資料到暫存器B中
            • 堆疊指標增1至8 0 0 0 h 

            對每個P U S H指令,堆疊都會增加2個位元組,這可能導致程式出現小毛病—堆疊可能會變得很大以致會覆蓋掉程式所需的一些程式碼和資料。這就是堆疊上溢問題。同樣,過多的P O P指令會過早用光堆疊內容,這就是堆疊下溢問題。

           如果8 0 8 0同一個6 4 K B的儲存器連線,你可能想把初始堆疊指標置為0 0 0 0 h。第一條P U S H指令使地址減1變為F F F F h,這時堆疊佔用了儲存器的最高地址。如果你的程式放在從0 0 0 0 h處開始的儲存器區域,則它和堆疊離的就太遠了。

           對堆疊暫存器進行賦值的指令是L X I,即load extended immediate(裝載擴充套件的立即數)。下面這些操作碼後的指令也是把兩個位元組裝載到1 6位暫存器:

      

                  L X I指令儲存一個位元組。另外,上表中最後一條L X I指令用來對堆疊指標賦值。微處理器復位後,這條指令並不常用來作為首先執行的指令之一:

                 

                 也可以對暫存器對和堆疊指標執行加1和減1操作,就好像它們是1 6位暫存器一樣:

                 即然是在討論1 6位指令,可以看看更多一些這樣的指令。下面的指令是把1 6位暫存器對的內容加到暫存器對H L中:

               上面這些指令可節約幾個位元組。例如,第一條指令正常需要6個位元組:

             D A D指令通常用於計算儲存器地址,這條指令隻影響C F。 

             下一步讓我們看以下各種指令。下面的兩個操作碼後都緊跟著一個2位元組地址,分別儲存和裝載暫存器對H L的內容:

             暫存器L的內容儲存在地址a a a a處,暫存器H的內容儲存在地址a a a a + 1處。 

             下面兩條指令用來從暫存器對H L中裝載程式計數器P C或堆疊指標S P:

             P C H L指令實際上是一種轉移指令, 8 0 8 0執行的下一條指令儲存在H L暫存器對中的地址所對應的儲存單元中。S P H L是另外一個設定S P的方法。

             下面兩條指令中,第一條指令使H L的內容與堆疊中最上面的兩個位元組進行交換,第二條指令使H L的內容與暫存器對D E的內容進行交換:

              除了P C H L外,還沒有講過8 0 8 0的轉移指令。前面第1 7章中講過,處理器中有一個叫作程式計數器P C的暫存器, P C中包含處理器取回並執行的指令的儲存器地址。通常P C使處理器順序執行儲存器中的指令,但有些指令—通常命名為J u m p(轉移)、B r a n c h(分支)或g o t o(跳轉)—能使處理器偏離這個固定的過程。這些指令使得P C裝載另外的值,處理器所取的下一條指令將在儲存器的其他位置。

              儘管簡單、普通的轉移指令很有用,但條件轉移指令更有用。這些指令可使處理器根據某些標誌,如C F或Z F,來轉移到另外的地址處。條件轉移指令的存在使得第1 7章中的自動加法機成為一般意義上的數字計算機。

             8 0 8 0有5個標誌位,其中4個對條件轉移指令有用處。8 0 8 0支援9種不同的轉移指令,包括無條件轉移指令和基於Z F、C F、P F、S F是1還是0的條件轉移指令。

            在介紹這些指令之前,先介紹一下與此相關的另外兩種指令。第一個是C a l l(呼叫)指令。C a l l指令與J u m p指令的不同之處在於:前者把一個新值裝入到程式計數器P C中,處理器儲存P C中原來的地址,儲存在哪裡?當然,在堆疊中。

            這種策略意味著C a l l指令可有效地儲存“程式從哪裡跳轉”的標記。處理器最終可利用此地址返回到原來的位置。這個返回指令叫R e t u r n。R e t u r n指令從堆疊中彈出兩個位元組,並把該值裝載到P C中。

            C a l l和R e t u r n指令是任何處理器中都很重要的功能。它們允許程式設計師編寫子程式,子程式是程式中經常用到的程式碼段。(“經常”一般意味著“不止一次”。)子程式是組合語言中的基本組成部分。

            讓我們看一個例子。假設你正在編寫一個組合語言程式,並且需要使兩個數相乘,因此你可以寫出一段程式碼來做這項工作,然後繼續往下編寫程式,現在又需要使兩個數相乘。因為你已知道如何進行兩數相乘,因此你只需簡單地重複使用同樣的指令來完成它。但只是簡單地兩次把這些指令輸入到儲存器嗎?希望不是,這是對時間和儲存空間的浪費,更好的方法是轉送到原來的程式碼處。由於無法返回到程式的當前位置,所以一般的J u m p指令不能用。但使用C a l l和R e t u r n指令可以讓你完成所需的功能。

           進行兩數相乘的一組指令可以作為一個子程式。下面就是這樣的子程式。在第1 7章中,被乘數(和結果)存放在儲存器的某一地址中;而在8 0 8 0子程式中,暫存器B的值和暫存器C中的值相乘,然後把1 6位乘積裝入暫存器H L中:

           注意,上述子程式的第1行開始有一個標號M u l t i p l y。當然,這個標號對應於子程式所在的儲存器地址。子程式開始用了兩個P U S H指令,通常在子程式開始處應先儲存(以後恢復)它需要使用的暫存器。 

          然後該子程式把H和L暫存器置為0。雖然可以使用M V I指令而不用S U B指令,但那需要使用4個位元組的指令而不是2個位元組的指令。子程式執行完後,暫存器對H L中儲存有相乘的結果。

         下一步該子程式把暫存器B的內容(乘數)移入A中,並且檢查它是否為0。如果它為0,乘法子程式到此結束,因為結果為0。由於暫存器H和L已經為0,因而子程式可以只使用J Z指令跳轉到末端的兩個P O P指令處。

         否則,子程式把暫存器B置為0。現在,暫存器對B C中包含一個1 6位的被乘數,A中為乘數。D A D指令把B C(被乘數)加到H L(結果)中。A中的乘數減1,且只要它不為0,J N Z指令就又使B C加到H L中。此小迴圈繼續下去,直到B C加到H L中的次數等於乘數。(可以用8 0 8 0的移位指令編寫一個更有效的乘法子程式。)

          利用這個子程式完成2 5 h與1 2 h相乘的程式用下面的程式碼:

         C a l l指令把P C的值儲存在堆疊中,該值是C a l l指令的下一條指令的地址。然後, C a l l指令使程式轉移到標號M u l t i p l y所標識的指令,即子程式的開始。當子程式計算完結果後,執行R E T(返回)指令,即從堆疊中彈出程式計數器的值,程式繼續執行C a l l指令後面的語句。

          8 0 8 0指令集中包括條件C A L L指令和條件R e t u r n指令,但它們遠不如條件轉移指令用得多。下表中完整地列出了這些指令:

           你可能知道,儲存器並不是唯一連線在微處理器上的裝置。一個計算機系統通常需要輸入輸出裝置以便於實現人機通訊。輸入輸出裝置通常包括鍵盤和顯示器。 

          微處理器是怎樣與外圍裝置(對於連線到微處理器而不是儲存器的東西的稱呼)進行通訊的呢?外圍裝置具有與儲存器相似的介面,微處理器可通過對應於外設的具體地址來對外設進行讀寫。在有些微處理器中,外圍裝置實際上佔用了通常用來定址儲存器的地址,這種配置叫作記憶體映像I / O。然而在8 0 8 0中,在65 536個正常地址外還有2 5 6個附加地址專門為輸入輸出裝置預留,這些就是I / O埠(I/O Port)。I / O地址訊號為A0~A7,但I / O訪問與儲存器訪問不同,由8 2 2 8系統控制晶片鎖存的訊號來區分。

          O U T指令用於把累加器中的資料寫到緊跟該指令的位元組所定址的I / O埠中。I N指令把埠的資料讀入到累加器中。

          外圍裝置有時需要引起微處理器的注意。例如,當你在鍵盤上按鍵時,如果微處理器能馬上知道這件事通常是有幫助的。這由稱作中斷(i n t e r r u p t)的機制來完成,這是連線至8 0 8 0 I N T輸入端的,由外設產生的訊號。

          然而,當8 0 8 0復位時,它不能對中斷產生響應。程式必須通過執行E I(Enable interrupts)指令來允許中斷,通過執行     D I(Disable Interrupts)指令來禁止中斷。

         

           8 0 8 0的 I N T E輸出端訊號表明允許中斷。當外設需要中斷微處理器當前工作時,它把 8 0 8 0的 I N T輸入端設定為 1 。 8 0 8 0通過從儲存器中取出指令對它作出響應,但控制訊號表明有中斷髮生。外設通常通過提供下述指令之一來響應 8 0 8 0:

           以上稱作 R e s t a r t指令,它們與 C A L L指令相似,也需要把當前程式計數器的值壓入堆疊。但R e s t a r t指令隨後轉移到一個特定的位置: RST 0 轉移到地址 0000h 處, RST 1 轉移到地址0 0 0 8 h處等等,直到 RST 7轉移到地址 0 0 3 8 h處。位於這些地址中的程式碼段來處理中斷。例如,來自鍵盤的中斷引起 RST 4 指令執行,地址 0 0 2 0 h處的一些程式碼從鍵盤讀取資料(這將在第 2 1章做全面介紹)。 

           到此為止,已講述了 2 4 3 個操作碼。下述 1 2個位元組與任何操作碼無關: 0 8 h、 1 0 h、 1 8 h、2 0 h、 2 8 h、 3 0 h、 3 8 h、 C B h、 D 9 h、 D D h、 E D h和F D h。這樣總共有 2 5 5個操作碼。下面還要提到一個操作碼:

           N O P代表 no op,即 no operation (無操作) 。 N O P指令使微處理器什麼都不做。這有什麼作用嗎?用於填空。 8 0 8 0通常可以執行一批 N O P指令而不會有任何壞情況發生。

           以下不打算再詳細討論 Motorola 6800 ,因為它的設計與功能與 8 0 8 0非常相似。下面是6 8 0 0的4 0個管腳:

             VS S代表接地, VC C是5 V電源。與 8 0 8 0相似, 6 8 0 0有1 6個地址輸出訊號和既可作為輸入又可作為輸出的 8個數據信號。它有 R E S E T訊號和 R /-W訊號。 -I R Q訊號代表中斷請求 。 6 8 0 0的時鐘訊號比 8 0 8 0的更加簡單。 6 8 0 0沒有 I / O埠的概念,所有輸入輸出裝置都必須是 6 8 0 0儲存器地址空間的一部分。

             6 8 0 0有一個 1 6位程式計數器 P C、一個 1 6位堆疊指標 S P、一個 8位狀態暫存器(作為標誌)以及兩個 8位累加器 A和B。它們都被看成是累加器( B不是隻作為一個暫存器)是因為沒有能用 A來做而不能用 B來做的事。 6 8 0 0沒有附加的 8位暫存器。

           6 8 0 0中有一個 1 6位索引暫存器( index register),可用來儲存一個 1 6位地址,很像 8 0 8 0中的暫存器對 H L。對於許多指令來說,它們的地址都可以由索引暫存器和緊跟在操作碼後的地址之和得到。

           雖然 6 8 0 0和 8 0 8 0所實現的操作相同— 裝載、儲存、加法、減法、移位、轉移、呼叫,但很明顯的區別是:它們的操作碼和助記符完全不同。例如,下面是 6 8 0 0的分支轉移指令:

            6 8 0 0沒有像 8 0 8 0中那樣的奇偶標誌位 P F,但它有一個 8 0 8 0中沒有的標誌位—溢位標誌位( overflow flag)。上述轉移指令中有些依賴於標誌位的組合。

            當然 8 0 8 0和 6 8 0 0指令集是不同的,這兩個晶片是同一時間由不同的兩個公司的兩組不同的工程師設計的。這種不相容性意味著每一種晶片不能執行對方的機器程式碼,為一種晶片開發的組合語言程式也不能翻譯成可在另一種晶片上執行的操作碼。編寫可在多於一種處理器上執行的計算機程式是第 2 4章的主題。

             8 0 8 0和 6 8 0 0還有一個有趣的不同點:在兩種微處理器中, L D A指令都是從一個特定的地址處裝載到累加器。例如,在 8 0 8 0中,下列位元組序列:
           將把儲存在地址 3 4 7 B h處的位元組裝載到累加器。現在把上述指令與 6 8 0 0的L D A指令相比較,後者採用稱作 6 8 0 0的擴充套件地址模式:

           該位元組序列把儲存在地址 7B34h 處的位元組裝載到累加器 A中。

           這種不同點是很微妙的。當然,你也可能認為它們的操作碼不同:對 8 0 8 0來說是 3 A h,對6 8 0 0來說是 B 6 h。但主要是這兩種微處理器處理緊隨操作碼後的地址是不同的, 8 0 8 0認為低位在前,高位在後; 6 8 0 0則認為高位在前,低位在後。

           這種 I n t e l和 M o t o r o l a微處理器儲存多位元組數時的根本不同從沒有得到解決。直到現在,I n t e l微處理器在儲存多位元組數時,仍是最低有效位元組在前(即在最低儲存地址處);而M o t o r o l a微處理器在儲存多位元組數時,仍是最高有效位元組在前。

            這兩種方法分別叫作 l i t t l e - e n d i a n ( I n t e l方式)和big-endian (Motorola方式)。辯論這兩種方式的優劣可能是很有趣的,不過在此之前,要知道術語 b i g - e n d i a n來自於 Jonathan Swift的《 G u l l i v e r’s Tr a v e l s》,指的是 Lilliput 和B l e f u s c u在每次吃雞蛋前都要互相碰一下。這種辯論可能是無目的的。先不說哪種方法在本質上說是不是正確的,但這種差別的確當在基於 l i t t l e endian 和b i g - e n d i a n機器的系統之間共享資訊時會帶來附加的相容性問題。

            這兩種微處理器後來怎樣了呢? 8 0 8 0用在一些人所謂的第一臺個人計算機上,不過可能更準確的說法是第一臺家用計算機上。下圖是 Altair 8800 ,出現在 1 9 7 5年1 月份的《 P o p u l a rE l e c t r o n i c s》雜誌的封面上。

           當你看到 Altair 8800 時,前面面板上的燈泡和開關看起來似乎很熟悉。這和第 1 6章為64KB RAM陣列建議的初始“控制面板”的介面是同一型別的。

            8 0 8 0之後出現了 Intel 8085 ,更具意義的是出現了 Z i l o g製造的 Z - 8 0晶片。 Z i l o g是Intel 公司的競爭對手,是由 I n t e l公司的前僱員,也曾在 4 0 0 4晶片上做出重要貢獻的 Federico Faggin建立的。 Z - 8 0與8 0 8 0完全相容,且增加了許多很有用的指令。 1 9 7 7年, Z - 8 0用於 Radio ShackTRS-80 Model1 上。

            也是在1 9 7 7年,由Steven Jobs 和Stephen Wo z n i a k建立的蘋果計算機公司推出了 APPLE II。APPLE II 既不用8080也不用 6 8 0 0,而是使用了採用 M O S技術的更便宜的 6 5 0 2晶片,它是對6 8 0 0的增強。

            1 9 7 8年6月, I n t e l公司推出了 8 0 8 6,一個 1 6位微處理器,它可訪問的儲存空間達到 1 M B。8 0 8 6的操作碼與        8 0 8 0不相容,但它包含乘法和除法指令。一年後, I n t e l公司又推出了 8 0 8 8,其內部結構與 8 0 8 6相同,但其外部按位元組訪問儲存器,因此該微處理器可使用較流行的為8 0 8 0設計的 8位支援晶片。 I B M在其5 1 5 0個人計算機—通常叫作 IBM PC—上使用了 8 0 8 8晶片,這種個人計算機在 1 9 8 1 年秋季推出。

            I B M進軍P C市場產生了巨大影響,許多公司都發布了與 P C相容的機器(相容的含義在隨後各章裡將要詳細討論)。多年來, “ IBM PC相容機”也暗指“ Intel inside”,特指所謂 x 8 6家族的 I n t e l微處理器。 Intel x86家族繼續發展, 1 9 8 5年出現了 3 2位的 3 8 6晶片, 1 9 8 9年出現了 4 8 6晶片。 1 9 9 3年初,出現了 Intel Pentium微處理器,普遍地用在 P C相容機上。雖然這些 I n t e l微處理器都不斷增加了指令的指令集,但它們仍然支援從 8 0 8 6開始的所有以前處理器的操作碼。

            蘋果公司的 Macintosh 首次釋出於 1 9 8 4年,它使用了 Motorola 68000—一個1 6位的微處理器,也即 6 8 0 0的下一代處理器。 6 8 0 0 0和它的後代(常稱為 6 8 K系列)是製造出的最受歡迎的一類微處理器。

            從1 9 9 4年開始, M a c i n t o s h計算機開始使用 Power PC, 一種由 M o t o r o l a、 I B M和A p p l e公司聯合開發的微處理器。 P o w e r P C是由一種稱作 R I S C(精簡指令集計算機)的微處理器體系結構來設計的,它試圖通過簡化某些方面以提高處理器的速度。在 R I S C計算機中,每條指令通常長度相同, (在 P o w e r P C中為 3 2位) ,儲存器訪問只限於裝載和儲存指令,且指令做簡單操作而不是複雜操作。 RISC 處理器通常有大量的暫存器以避免頻繁訪問儲存器。

            因為 P o w e r P C具有完全不同的指令集,所以它不能執行 6 8 K的程式碼。但現在用於 A P P L EM a c i n t o s h的 PowerPC 微處理器可模擬 6 8 K。運行於 PowerPC 上的模擬程式逐個檢驗 6 8 K程式的每一個操作碼,並執行適當的操作。模擬程式不如 P o w e r P C自身程式碼那樣快,但可以工作。

            按照摩爾定律,微處理器中的電晶體數量應該每 1 8個月翻一番。這些多增加的電晶體有什麼用處呢?

            有些電晶體用於增加處理器的資料寬度,從 4位到8位到1 6位再到3 2位;另外一些增加的電晶體用於處理新的指令。現在大多數微處理器都有用於浮點算術運算的指令(這將在第 2 3 章解釋);還有一些新增加的指令用來進行一些重複計算,以便在計算機螢幕上顯示圖片和電影。

            現代處理器使用了一些技術用來提高速度,其中之一是流水線技術,處理器在執行一條指令的同時讀取下一條指令。由於轉移指令會改變執行流程,實際上這樣達不到預期效果。現在的處理器還包含一個 Cache( 高速緩衝儲存器 ),它是做在處理器內部的快速 R A M陣列,用於儲存最近執行的指令。因為計算機程式經常執行一些小的指令迴圈,因而 C a c h e可以避免這些指令重複裝載。所有這些速度提升措施都需要在處理器中有更多的邏輯器件和電晶體。

            如前所述,微處理器只是完整的計算機系統的一部分(儘管是最重要的部分)。我們將在第2 1 章構造這樣一個系統,但首先必須學習怎樣編碼存在儲存器中的除了操作碼和數字外的其他東西。我們必須返回到小學一年級,再學習一下怎樣讀寫文字。