目錄
王爽《彙編語言》第四版 超級筆記
第18章 附注内容
18.1 Intel系列微處理器的3種工作模式
18.2 補碼
18.3 用棧傳遞參數
微機中常用的Intel系列微處理器的主要發展過程是:8080,8086/8088,80186,80286,80386,80486,Pentium,PentiumII,Pentiumlll,Pentium4。
8086/8088是一個重要的階段,8086和8088是略有差別的兩個功能相同的CPU。
8088被IBM用在了它所生産的第一台微機上,該微機的結構事實上成為以後微機的基本結構。
80386是第二個重要的型号,随着微機應用及性能的發展,在微機上構造可靠的多任務作業系統的問題日益突出。人們希望(或許是一種潛在的希望,一旦被挖掘出來,便形成了一個最基本的需求)自己的PC機能夠穩定地同時運作多個程式,同時處理多項工作;或将PC機用作主機伺服器,運作UNIX那樣的多使用者系統。
8086/8088不具備實作一個完善的多任務作業系統的功能。為此Intel開發了80286,80286具備了對多任務系統的支援。但對8086/8088的相容卻做得不好。這妨礙了使用者對原8086機上的程式的使用。IBM最早基于80286開發了多任務系統OS/2,結果犯了一個戰略錯誤。
随後Intel又開發了80386微處理器,這是一個劃時代的産品。它可以在以下3個模式下工作。
(1)實模式:工作方式相當于一個8086。
(2)保護模式:提供支援多任務環境的工作方式,建立保護機制(這與VAX等小型機 類似)。
(3)虛拟8086模式:可從保護模式切換至其中的一種8086工作方式。這種方式的提供使使用者可以友善地在保護模式下運作一個或多個原8086程式。
以後的各代微處理器都提供了上述3種工作模式。
你也許會說:“喂,先生,你說的太抽象了,這3種模式我如何感覺?”
其實CPU的這3種模式隻要用過PC機的人都經曆過。任何一台使用Intel系列CPU的PC機隻要一開機,CPU就工作在實模式下。如果你的機器裝的是DOS,那麼在DOS加載後CPU仍以實模式工作。如果你的機器裝的是Windows,那麼Windows加載後,将由Windows将CPU切換到保護模式下工作,因為Windows是多任務系統,它必須在保護模式下運作。如果你在Windows中運作一個DOS下的程式,那麼Windows将CPU切換到虛拟8086模式下運作該程式。或者是這樣,你點選開始菜單在程式項中進入MS-DOS方式,這時Windows也将CPU切換到虛拟8086模式下運作。
可以從保護模式直接進入能運作原8086程式的虛拟8086模式是很有意義的,這為使用者提供了一種機制,可以在現有的多任務系統中友善地運作原8086系統中的程式。這一點,在Windows中我們都可以體會到,你在Windows中想運作一個原DOS中的程式,隻用滑鼠點選一下它的圖示即可。
80286CPU的缺陷在于,它隻提供了實模式和保護模式,但沒有提供虛拟8086模式。這使基于80286構造的多任務系統,不能友善地運作原8086系統中的程式。如果運作原8086系統中的程式,需要重新啟動計算機,使CPU工作在實模式下才行。這意味着什麼?
意味着将給使用者造成很大的不友善。假設你使用的是基于80286構造的Windows系統,就會發生這樣的情況:你正在用Word寫一篇論文,其中用到了一些從前的資料,你必須運作原DOS下的DBASE系統來看一下這些資料。這時你隻能停下現有的工作,重新啟動計算機,進入實模式工作。你看完了資料,繼續寫論文,可過了一會兒,你發現又有些資料需要參考,于是你又得停下現有的工作,重新啟動計算機……
幸運的是,我們用的Windows是基于80386的,我們可以以這樣輕松的方式工作,開兩個視窗,一個是工作于保護模式的Word,一個是工作于虛拟8086模式的DBASE,我們可以友善地在兩個視窗中切換,隻要用滑鼠點一下就行。
前面講過,我們在8086PC機的基礎上學習彙編語言。但現在知道,我們實際的程式設計環境是目前CPU的實模式。當然,有些程式也可以在虛拟8086模式下運作。
如果你仔細閱讀了上面的内容,或己具備相關的知識,你會發現,從80386到目前的CPU,提供8086實模式的目的是為了相容。現今CPU的真正有效力的工作模式是支援多任務作業系統的保護模式。這也許會引發你的一個疑問:“為什麼我們不在保護模式下學習彙編語言?”
類似的問題很多,我們都希望學習更新的東西,但學習的過程是客觀的。任何合理的學習過程(盡可能排除走彎路、盲目探索、不成系統)都是一個循序漸進的過程。我們必須先通過一個易于全面把握的事物,來學習和探索一般的規律和方法。資訊技術是一個發展非常快、日新月異的技術,新的東西不斷出現,使人在學習的時候往往無所适從。
在你的身邊不斷有這樣的故事出現:COOL先生用了3天(或更短)的時間就學會了某某語言,并開始用它編寫軟體。在這個故事的感召下,一個初學者也去嘗試,但完全是另外一種結果。COOL先生的快速學習隻是露出水面的冰山一角,深藏水下的是他的較為系統的相關基礎知識和相關的技術。在開始的時候學習保護模式下的程式設計,是不現實的,保護模式下所涉及的東西對初學者來說太複雜。你必須知道很多知識後,才能開始編寫第一個小程式。相比之下8086就合适得多。
以8位的資料為例,對于無符号數來說是從00000000b~11111111b到0-255一一對應的。那麼我們如何對有符号數進行編碼呢?即我們如何用8位資料表示有符号數呢?
既然表示的數有符号,則必須要能夠區分正、負。
首先,我們可以考慮用8位資料的最高位來表示符号,1表示負,0表示正,而用其他位表示數值。如下:
00000000b: 0 00000001b: 1 00000010b: 2 01111111b: 127 10000000b: ? 10000001b: -1 10000010b: -2 11111111b: -127
可見,用上面的表示方法,8位資料可以表示-127-127的254個有符号數。從這裡我們看出一些問題,8位資料可以表示255種不同的資訊,也就是說應該可以表示255個有符号數,可用上面的方法,隻能表示254個有符号數。注意,用上面的方法,00000000b和10000000b都表示0,一個是0,一個是-0,當然不可能有-0。可以看出,這種表示有符号數的方法是有問題的,它并不能正确地表示有符号數。
我們再考慮用反碼來表示,這種思想是,我們先确定用00000000b〜01111111b表示0~127,然後再用它們按位取反後的資料表示負數。如下:

可以看出,用反碼表示有符号數存在同樣的問題,0出現重碼。
為了解決這種問題,釆用一種稱為補碼的編碼方法。這種思想是:先确定用00000000b〜01111111b表示0~127,然後再用它們按位取反加1後的資料表示負數。 如下:
觀察上面的資料,我們可以發現,在補碼方案中:
(1)最高位為1,表示負數;
(2)正數的補碼取反加1後,為其對應的負數的補碼;負數的補碼取反加1後,為其絕對值。比如:
1的補碼為:00000001b,取反加1後為:11111111b,表示T;
T的補碼為:11111111b,取反加1後為:00000001b,其絕對值為1。
我們從一個負數的補碼不太容易看出它所表示的資料,比如:11010101b表示的資料是多少?
但是我們利用補碼的特性,将11010101b取反加1後為:00101011b。可知11010101b表示的負數的絕對值為:2BH,則11010101b表示的負數為-2BH。
那麼-20的補碼是多少呢?
用補碼的特性,-20的絕對值是20,00010100b,将其取反加1後為:11101100b。可知-20H的補碼為:11101100b。
那麼10000000b表示多少呢?
10000000b取反加1後為:10000000b,其大小為128,是以10000000b表示-128。
8位補碼所表示的數的範圍:-128~127。
補碼為有符号數的運算提供了友善,運算後的結果依舊滿足補碼規則。
比如:
計算 補碼表示
10 00001010b
+ (-20) 11101100b
-10 11110110b
這種技術和進階語言編譯器的工作原理密切相關。我們下面結合c語言的函數調用,看一下用棧傳遞參數的思想。
用棧傳遞參數的原理十分簡單,就是由調用者将需要傳遞給子程式的參數壓入棧中,子程式從棧中取得參數。我們看下面的例子。
;說明:計算(a-b)^3,a、b為字型資料
;參數:進入子程式時,棧頂存放IP,後面依次存放a、b
;結果:(dx:ax) = (a-b)^3
指令retn的含義用彙編文法描述為:
pop ip add sp,n
因為用棧傳遞參數,是以調用者在調用程式的時候要向棧中壓入參數,子程式在傳回的時候可以用ret n指令将棧頂指針修改為調用前的值。調用上面的子程式之前,需要壓入兩個參數,是以用ret 4傳回。
我們看一下如何調用上面的程式,設a=3、b=1,下面的程式段計算(a-b)^3:
mov ax,1 push ax mov ax,3 push ax ;注意參數壓棧的順序 call difcube
程式的執行過程中棧的變化如下。
(1)假設棧的初始情況如下:
(2)執行以下指令:
(3)執行指令 call difcube,棧的情況變為:
(4)執行指令push bp,棧的情況變為:
(5)執行指令mov bp,sp,ss:bp指向1000:8
(6)執行以下指令:
mov ax,[bp+4];将棧中a的值送入ax中 sub ax,[bp+6];減棧中b的值 mov bp,ax mul bp
(7)執行指令pop bp,棧的情況變為:
(8)執行指令ret 4,棧的情況變為:
下面,我們通過一個C語言程式編譯後的彙編語言程式,看一下棧在參數傳遞中的應用。要注意的是,在C語言中局部變量也在棧中存儲。
C程式
編譯後的彙程式設計式