顯示具有 計算機結構 標籤的文章。 顯示所有文章
顯示具有 計算機結構 標籤的文章。 顯示所有文章

2017年12月8日 星期五

淺談古代CPU:CISC廠的RISC(1)

RISC以及隨之而來的CISC這兩個縮寫的爭論與演變,其實是個很有趣的話題,RISC這個縮寫跟觀念出現前,指令集就是指令集,各家做各家的,也沒人計較誰比較複雜或簡單;RISC出現後,倒也沒有聽說哪家故意去違背潮流,推出「全新的CISC指令集」處理器。

不過,對於那些在RISC出現前就做電腦系統的「老」廠商們來說,怎麼面對這個RISC新潮流,是個頗堪玩味的問題,有的是毅然決然完全放棄過去的CISC,擁抱全新的未來;有的則是騎驢找馬,舊的照做,也做個新的試試手氣;至於以下這個廠商,八卦可精彩了....

身為微處理器的開路先鋒和領頭羊,Intel一開始對RISC幾乎是無動於衷,沒有野心?錯了,Intel的野心一點都不小,1974年的8080銷路極佳,讓Intel賺了不少錢,從1975年開始,Intel執行一項非常巨大的計畫,代號是8800,後來改名iAPX 432。

iAPX 432有多巨大?首先,Intel替這計畫訂的目標是「可以滿足未來十年內所有可能的客戶需求」,它的假想敵不是同級的微處理器,而是當時最佳的32位元大型主機,你現在想得到的多處理器、容錯、多工作業系統,物件導向記憶體管理等等等等,都被納入這個計畫的目標,它是Intel第一個32bit處理器,而當時Intel的8008和8080都還只是8位元而已;由於目標非常巨大,開發需要很長的時間和龐大的經費,為了彌補財源,也為了應付一旁虎視眈眈的Motorola、Zolog等微處理機同業,Intel勉強湊出一個團隊,用最快的速度急就章的做了一顆在組合語言階層上可以相容於8080的16位元處理器,當作正餐上桌前的開胃菜,這道開胃菜就是各位所熟知的8086。

8086在1976年問世,1979年,Intel把8086的資料線縮到8bit,推出8088,搶到了IBM PC這塊同樣是急就章的肥肉,展開了PC大航海時代,到了1981年,Intel終於端出第一套iAPX 432系統,結果是...慢,慘絕人寰的慢。

由於當初設定的目標,432的指令集「非常非常CISC」,首先,432的指令長度不固定,可以從6 bits到321 bits,對,你沒看錯,是「bit」,不是「byte」;除了複雜的指令集以外,極端複雜的物件導向記憶體管理(還自備Garbage-collection機制喔),以及容錯導致的匯流排等待時間都是導致速度拖慢的原因,更有甚者,因為結構的複雜,以當時的製程,432必須分成兩顆晶片,晶片間的速度慢以外,上面還沒甚麼空間可以塞cache,結果就是,Intel這顆壓箱寶,比當時同樣是32bit的Motorola 68000慢了5到10倍。

就在432端出來被嘲笑的同時,RISC這個新縮寫也同時浮上檯面;1980年,由加州大學柏克萊分校的David Patterson為首,發表了RISC I(順便也創造了RISC這個縮寫),1981年,由史丹福大學John L. Hennessy 為首設計的MIPS問世;在此同時,Intel還是不死心,1982年,Intel推出第二道墊檔開胃菜80286,同時繼續企圖挽救432。

1984年,德國西門子(Siemens)公司找上了Intel合作,共同出資開設一間新公司,名字叫BiiN, BiiN 的目標是開發一套高效能,具備容錯功能的高可靠度電腦系統,鎖定銀行、軍方或是像核電廠控制電腦這類「關鍵任務」的特定市場,Intel的任務,就是替這套電腦做出一顆處理器。

想當然爾,跟扶不起的432相比,BiiN顯然重要得多,所以原來參與432計畫的大部分人員都被調去做BiiN,其中還包括一個從IBM借來的 Glenford Myers,此君寫過一本有名的電腦科學教科書「軟體測試的藝術」The Art of Software Testing,擁有多項微處理器專利,包括微處理器上的暫存器 Scoreboarding設計;他在1981年初被Intel從IBM那邊找來做一個代號「P4」的計畫,企圖對抗Motorola的68000(以程式設計的角度,68000遠比Intel的x86「優雅」很多),後來IBM PC還是用了8086的變形8088,所以P4無疾而終;但是Myers還是參與多項Intel微處理器設計的重大決策,包括後來的386,上面提到的iAPX432,以後會提到的i860;

有了432這個前車之鑑,BiiN這顆代號P7的處理器當然不會再使用過度複雜的指令集,相反的,BiiN這顆處理器指令集有相當大的一部份參考了柏克萊RISC I的設計:指令固定為32bit長(只有MEM_B型式的指令是32bit再加上一個32bit的位移值),16個32bit的全域暫存器,以及16個區域暫存器,區域暫存器其實有四組,這是RISC I的Register Window設計,另外還有四個80bit長的浮點暫存器;雖然只有4個浮點暫存器,但是實際上BiiN這顆處理器的浮點運算指令可以用到「所有的」暫存器,浮點指令裡面有個欄位,可以分別切換要使用整數或浮點暫存器。


除了load-store相關指令以外,所有運算都在暫存器裡面執行;但是因為需要容錯和記憶體保護等功能,他們把原來i432的物件導向記憶體管理簡化後,移植到這顆處理器;第一顆能動的晶片在1985年年底出廠,剛好跟386差不多時間。

雖然這顆晶片效能還不錯,但是就在這個時候,BiiN 上面兩家母公司開始對這個系統的前景有點擔憂,畢竟,一個系統包括了軟體和硬體,只有晶片完成是不夠的;Myers看到大事不妙,拚了命的遊說Intel高層,希望可以把已經完成的P7晶片拿回Intel,乾脆就取代286和386,成為下一代的汎用處理器,也順便可以讓Intel進軍Unix市場,他同時也把整個BiiN的硬體整理歸納出幾個可拆解的大項;雖然高層沒有同意這麼激進的意見,但是最後這個晶片的基本架構被保留了下來,編號改為i960,目標是放在嵌入式系統市場;不過Glenford Myers自己在1987年時也離開了Intel,跟i960的晶片經理Dave Budde一起開了一家新公司 RadiSys ,而i960由另外一位原本i432的工程師,超純量結構專家 Fred Pollack 接手。

i960有好幾種不同的款式和編號,首先是具備原本BiiN全部功能的i960MX,這顆晶片沒有賣到一般市面上,另外有一顆i960MC,功能上其實一模一樣,但是所有比較先進的功能,文件裡面都省略不提,很多拿到i960MC的客戶會覺得很奇怪,怎麼會有這麼多接腳標明「no connect」....

拿掉複雜的記憶體管理系統後的i960編號80960Kx,編號有兩款,KA和KB,理論上KA沒有浮點運算器,但是其實KA和KB完全一樣,只是改個IC標籤而已,很多雷射印表機用這顆處理器,另外,1993年Sega製造的大型電玩機台的主機板Sega Model 2也是使用i960KB作為主處理器。

80960Cx在1989年七月發表,核心經過重新設計,一個週期可以發送三個指令執行,所以960Cx是Intel第一顆「超純量」(Superscalar)微處理器;事實上,頭三個商用超純量微處理器:1988年Motorola的88100、1989年的i960CA,以及1990年AMD的AM29050,很意外地都是「CISC廠的RISC」。

1990年代,Intel替i960找到一條新的出路:在PC市場當作高效能的I/O處理器;Intel還特別提出一個稱為I2O的開放規格,以i960做為中心,可以用來降低主處理器處理I/O時的負擔,不過因為i960價格實在不便宜,這個規格推了幾年後也無疾而終;另外,因為i960運算速度不算慢,尤其執行像是XOR運算,這對很多硬碟陣列(RAID)來說很好用,所以也有不少硬碟陣列卡採用i960。

雖然在嵌入式市場表現亮眼,但是在1997年,Intel買下了原本DEC的StrongARM後,i960(以及同樣是RISC的i860)就漸漸淡出舞台,事實上,早在1990年左右,主要的i960開發團隊,包括 Fred Pollack在內,就被指派去作為開發i386架構的「次世代」微處理器,後來代號P6的Pentium Pro,也就是現在Intel所有core架構的起點。

i960就這樣結束了?偷偷告訴你,有一家休斯飛機公司把i960(事實上就是BiiN)的整個硬體設計授權買了下來,用在一個稱為ATF(先進戰術戰鬥機)的計畫裡,也就是後來的F-22,換句話說,世界最強的戰鬥機裡面,可能還有幾顆i960正在全力工作。

2017年9月16日 星期六

簡介RISC與CISC

前言

這是一篇為了非科班但是對RISC與對應的CISC這兩個縮寫及相關知識有興趣的同好所寫的入門文章,嚴格來說,這個領域其實是「計算機結構」的一部份,如果想要真正的完全了解,請研讀或參閱正規的教科書和課程。

電腦基本結構

開宗明義,RISC是 Reduced Instruction Set Computer的縮寫 ,翻譯為「精簡指令集電腦」,CISC則是相對的縮寫(Complex Instruction Set Computer,「複雜」指令集電腦),所以要完整的解釋這兩個縮寫,必須先解釋兩件事:首先,什麼是電腦?第二、什麼是電腦的「指令集」,最後才能解釋什麼是「擁有精簡/複雜指令集的電腦」。

以最簡單的方式來說,電腦是一種「可以記憶並處理(或者說運算)資訊的機器」;很多人都用過算盤或是那種只有數字和加減乘除的掌上型計算機,當需要計算很多數字,例如把幾十個數字各自照公式計算然後平均等等,會怎麼做呢?首先,你要有張紙,上面記錄要計算的數字,以及計算的公式,你看一眼公式,把數字打進計算機或算盤,根據公式計算,然後把計算結果寫回那張紙,完成工作;恭喜你,你做的事情就是現代電腦所做的事情。

在電腦出現前,也有很多同樣具備記憶和處理資訊能力的機器,例如算盤,但是電腦與之前這些「計算機器」最大的不同,在於電腦可以將「指揮電腦如何運算」當作是一種可以儲存及修改的資訊,換句話說,就是那張「紙」,以及從「紙上讀取公式和資料」的能力,讓電腦有了超越以往計算機器的能力。

在電腦中,那張上面寫了計算公式和數字的紙,稱為「記憶體」,無論是公式還是數字,都在這張紙上,而計算的結果最後也是寫回這張紙,但是記憶體跟真正的紙有點不太一樣,你可以把記憶體想像成一個黑盒子,裡面分成很多格,但是你一次只能讀或寫其中的一格,所以你需要給這個黑盒子一串數字,告訴它你現在要讀寫哪一格,這串數字稱為記憶體的「位址」(address)。
(當然,記憶體不是只有這種形式,不過這是最簡單以及最常用的一種機制)

至於從記憶體讀取公式和數字並加以計算的部份,古早以前,這部分分為控制和運算兩種單元,控制單元從記憶體中讀取公式和數字,然後送給運算單元產生結果,並將結果寫回記憶體,因為這兩個單元關係很密切,通常我們把這兩者合稱為「中央處理器」,也就是大家熟知的「CPU」,當然,這是很古老的說法,現在看到的「CPU」已經加了很多其他元件進去,但是基本上這兩個元件還在。


既然叫「電」腦,可想而知,你在計算數字時所做的:用眼睛看紙張上的符號、用手拿筆在紙上紀錄,這些動作,或是說「資訊的傳遞」,在電腦中都是用電來完成(至少目前是如此),正確的說法是藉由在電線上傳遞不同高低電壓的訊號(或是用電磁波,但是無論如何,原理是一樣的),電腦中不同的單元會互相傳送電磁訊號,所以以下所謂的「讀取」、「寫入」等等動作,其實就是藉由電磁訊號進行的資訊傳遞。

指令、指令集

電腦的基本運作方式如下:首先,CPU會先讀取記憶體的某格,得到一種「指揮電腦如何運算」的資訊,接著根據這個資訊,再從記憶體讀取要被處理的資料,並加以運算,運算後的結果再寫回記憶體,接著再讀取下一個指揮運算的資訊;這種「指揮電腦如何運算」的資訊稱為「指令」(Instruction),;一台電腦裡用來指揮運算的一連串指令和資料的組合,我們稱為「程式」所以我們現在的電腦有另外一個很拗口的名字,叫「內儲程式計算機」。
(另外,因為一些美麗的歷史錯誤,這個架構有時候會被冠上偉大的數學家John von Neumann的名字,被稱為von Neumann Architecture,有興趣的可以參閱維基百科相關介紹)

指令本身由特定的規則組成,而一台電腦所能接受的所有指令規則組合,我們稱為「指令集」(Instruction Set),指令集可以被視為是電腦所能了解的「語法」,要指揮一台電腦進行運算,程式的指令必須符合這台電腦的指令集規則,才能在這台電腦正確的執行;

不同廠商所設計的電腦系統,可能會有不同的指令集,而一種程式如果是以某種指令集語法所構成的話,通常也無法直接在另外一種指令集的電腦系統下執行;另外,即使是同一個廠商的電腦系統,新的系統有可能會在原有的指令集中加入新的指令格式與規則,所以為了新系統所寫的程式不見得可以在舊的系統下順利執行,如果系統可以執行舊系統的程式,我們稱這兩個指令集是「相容」的,以我們目前個人電腦的x86指令集為例,這個指令集雖然是Intel發展的,但是因為早期的一些商業協議(以及背後殘酷的專利與法律交鋒),AMD公司也可以製造有相容指令集的微處理器。


RISC之前

在電腦的發展初期,由於硬體能力的限制,指令集通常不太複雜,其實也沒有辦法太複雜,另一方面,當時高階語言和編譯器也還不成熟,所以多半還是以機器語言和組合語言為主,很快的,程式設計師發現這些指令不太夠用,不夠用並不是寫不出程式,而是有些比較複雜的程式,要用一大堆指令才能寫出來,但是在那個記憶體寸土寸金的年代,程式本身的大小當然越小越好,所以為了能做更多事情,電腦的結構和指令集有了一些改變。

首先,每個指令都要讀取和寫入記憶體這點其實很麻煩,剛剛上面提過,讀寫記憶體分成兩個步驟:先送出位址,再讀寫資料,一個典型的運算,例如 A = B + C ,需要讀取兩筆資料,寫入一筆,要做三次記憶體存取,指令本身就要容納三個位址,所以後來想出了一個方法:在CPU裡面另外增加一塊特別的記憶體,除了原先的指令以外,增加一些新的指令,可以指定運算的結果直接寫進這塊記憶體,也可以直接使用這塊記憶體的資料當作輸入,因為只有一個或兩個,所以不需要很長的位址就可以指定,指令本身長度就可以變短,換句話說如果多使用這些指令,程式就會比較小;這種特別的記憶體稱為「累加器」(accumulator),後來數量變多,也不限制只能存放運算後的結果,演變成一種存放「暫時放在CPU裡面的資料」的記憶體,稱為暫存器(registers)。

另外一方面,隨著越來越複雜的應用,指令集本身也變得越來越複雜,但是複雜的指令集表示要用複雜的電路來完成,對當時的電子工藝來說非常困難,所幸這個時候「微碼」(Microcode或是Microprogram)誕生,微碼是位於CPU內部的小程式,每個指令被讀進CPU後,指令的不同部分進一步被分解成對應的一系列微程式,再由CPU真正的電路一步步地完成,微碼的出現,一方面降低CPU電路設計的複雜度,另一方面也容許指令集提供更多複雜的指令給程式設計師,所以到了七零年代,大部分的電腦系統都採用了微碼設計,指令集也就越來越複雜。

為了解釋這些電腦的指令有多「複雜」,我們以DEC (Digital Equipment Corporation,迪吉多電腦公司)的VAX的指令集作為例子,VAX是DEC在七零年代中後期(精確的說,是1977年),以PDP-11為基礎發展出的小型32位元電腦系統,是當年很暢銷的電腦主機之一。

VAX的指令集非常的「正交」(orthogonal),這是個專有名詞,意思是指令格式中的不同部分,特別是指定運算資料來源的「運算元」部分非常的「各自獨立」;以VAX的算術運算為例,基本上有兩類,一類是兩個運算元,一類是三個運算元,例如加法:

A = A + B,這樣是兩個運算元(A、B);
C = A + B,就是三個運算元(A、B、C)
(這是用高階語言的表示法,真正的樣子是一串0與1的數字格式和代碼,這裡不多做解釋)

VAX的指令中,這些運算元A、B、C可以分別是暫存器或是用暫存器定址的記憶體,而且定址還不只一種模式,事實上光是以暫存器為基礎的記憶體定址模式就有十多種,而且一個指令中不同運算元的定址模式是各自獨立的,換句話說程式設計師可以任意、分別指定這些運算元在暫存器或是記憶體的某個位址,這對於寫組合語言來說很方便,但是硬體上可就麻煩透頂,首先,因為不知道指令有幾個運算元,前面的OPcode要先解碼完,才能接著把指令剩下的部分抓進去;接著,這些運算元要分別去解碼判定運算元定址方式,如果很不幸的都是記憶體,還要個別做記憶體位址的計算、轉換和存取,這些複雜的工作當然不可能直接用硬體一次搞定,所以需要用指令轉換成一連串內部的微程式,代價就是指令執行時間變長,而且不是只有那些長指令,即使是只使用暫存器的短指令,也因為微程式的關係導致變慢。

在指令集複雜化的同時,電子工藝和程式設計方式也有了改變;1960年代積體電路(IC)開始取代之前的電晶體,成為電腦的主要元件;七零到八零年代,IC演變為大型積體電路(VLSI),原先需要好幾片電路板的元件,現在可以用幾塊甚至單一一塊晶片就可以完成;而程式設計也從之前以組合語言為主,變成以編譯程式編譯的高階語言為主。

RISC的出現

1980年,加州大學柏克萊分校的David Patterson教授發表了著名的論文:"The case for Reduced Instruction Set Computer",在這篇論文中,Patterson和他的學生們分析了當時幾種主流電腦,包括上述的VAX、IBM  system/360等系統的指令集,以及幾種標準測試程式在這些電腦系統上編譯後的機械碼之後,歸納出以下的結論:
  • 大部分的高階語言程式所編譯出來的機械碼中,只會用到指令集中少部分,而且是比較簡單的指令。
  • 微碼和指令集的複雜化導致的不合理設計;舉例來說,VAX有個叫 INDEX的指令,用來計算陣列位址,同時檢查位址是不是超過陣列的邊界範圍,這個指令可以用幾個其他簡單指令做到,而且還比較快。
  • 電腦系統積體電路化的結果,會讓CPU內部速度與外部記憶體存取的速度差異越來越大。
根據這些結果,他們提出一個新的指令集設計概念,主張:

  • 指令集的設計應該盡可能的精簡,只有最常用、功能最單純的指令會被放入指令集,而且因應高階語言的普及,應該以能夠配合高階語言編譯器為主。
  • 盡可能不使用微碼以降低設計複雜度與提高執行速度。
  • 配置大量暫存器以降低對外部記憶體的存取頻率,同時只使用特定的指令存取外部記憶體,其他一般運算指令都使用暫存器作為運算元。
  • 所有的指令(除了記憶體存取相關的以外)盡量在一個機器週期(machine cycle)內完成。
他們把這樣的指令集設計的電腦系統稱為「精簡指令集電腦」,也就是 RISC,而之前這些電腦系統則稱為「複雜指令集電腦」(CISC);根據RISC概念,他們提出了一套指令集,就是所謂的Berkeley RISC,並在次年(1981年)時實作出第一顆RISC微處理器:RISC I。

RISC I


RISC I是顆32位元的處理器,指令長度也固定為32位元,只有31個指令,包括基本的加減法、移位和邏輯運算,沒有乘法指令(因為以當時的製程來說,乘法的運算電路太大太慢,而且乘法基本上可以用移位和加法指令取代);外部記憶體存取只限於load/store這兩種指令;這顆處理器有44420個電晶體,以5um NMOS製程製造,整顆晶片裡指令解碼和運算電路只佔了晶片面積的6%,其他都是暫存器(SRAM),RISC I一共有78個32位元暫存器,分為六組,這就是所謂的「暫存器框格」(Register Window)設計,這個機制比較有意思,它的原意是希望降低呼叫副程式時的暫存器和記憶體間的資料傳輸,但是這個機制本身其實也有點麻煩,所以只有幾家少數的RISC用過(RISC I、SUN SPARC以及Intel的i960),大部分其他RISC,像等一下會提到的MIPS就沒有。

雖然David Patterson他們創造了RISC這個縮寫,但是類似的概念其實很早就出現過;早在1965年,控制資料公司(Control Data Corp., CDC)的旗艦級大型電腦主機CDC6600也採用了類似的指令集設計概念;而1975年,由John Cocke領導設計的實驗性迷你電腦主機:IBM 801也是如此,但是都算是當時的「非主流」設計,並沒有獲得廣泛的重視;IBM 801的基本設計後來成為IBM  RS/6000以及著名的IBM POWER架構、Apple後來在Power Mac所使用的PowerPC RISC微處理器,以及SONY PS3所使用的cell處理器的基礎。

(不過CDC6600和IBM 801都不是RISC「微處理器」VAX雖然是CISC,但是也不是CISC「微處理器」,一開始我們有提到電腦的主要元件「CPU」,在一般人的印象裡,CPU就等於「微處理器」,是一塊晶片,但是在電腦發展的早期,CPU其實是好幾櫃子的電路板和真空管或是電晶體,加上密密麻麻的電線,後來技術進步後可以縮減到一片電路板以及上面的許多顆IC,CDC6600大約就是那時候的電腦,要到1971年,才由我們熟知的Intel公司,把一顆簡單的4 bit CPU所需要的全部元件放進一片有2300顆電晶體的晶片裡,做出第一顆「微處理器」4004,但是那時候微處理器的功能和速度還遠不是大型電腦主機的對手)

CDC 6600


在RISC I提出的同時,史丹福大學的John L. Hennessy教授提出了另外一套概念類似的指令集,稱為MIPS(其實這是個很拗口的縮寫:Microprocessor without Interlocked Pipeline Stages,無互鎖階段管線的微處理器),1984年開設了MIPS公司並量產R2000微處理器,MIPS的微處理器在一些高階繪圖工作站以及嵌入式系統被使用;後來這兩位「RISC教授」更合寫了兩本有名的計算機結構與組織教科書:Computer Architecture: A Quantitative Approach以及Computer Organization and Design: the Hardware/Software Interface。這兩本現在是很多大學資訊科系計算機結構相關課程的經典教科書。

到了1986年以後,幾乎所有當時的電腦系統廠商都推出過基於RISC概念的微處理器,有些廠商甚至直接放棄了自己原有的CISC系統;到了九零年代以後,即使是像Intel x86這樣的老牌CISC微處理器,在內部的設計上也或多或少的受到RISC設計概念的影響,如果有興趣,可以參閱本文作者所寫的這篇「淺談古代CPU:CISC廠的RISC(1)」

附帶一提,1983年,有家叫Acorn的英國小公司,因為找不到合用的微處理器,他們參考了RISC-I的相關論文,自己發展了一個稱為Acorn RISC Machine的架構,並在1985年做出第一個實作ARM2,這家公司後來改名Advanced RISC Machine,這個架構就是各位手上的手機和平板所用的各種ARM處理器的前身。

補充:有關RISC的一些常見誤解

  • RISC只是CPU「指令集」的一種設計概念,在電腦和CPU這塊領域,還有很多奇奇怪怪的術語和縮寫,像是多處理器或雙核心四核心、SMT、管線(pipeline)、Superscalar等等等等,這些都跟「指令集設計」沒有直接的關係,那些術語是實作CPU時的某些機制,跟是不是CISC或是RISC沒有關係;另外像是CPU的製程或時脈,跟RISC/CISC也沒有直接的關係。
  • 微碼/微程式並不是「因為」VLSI出現才消失的,以現在的電子工藝,大可以用VLSI做一片「使用微碼的超級CISC」微處理器,只是不會比較快而已。
  • 後來為了因應需要,新的RISC有加入一些新指令,像是對應多媒體應用而產生的向量運算等等之類的,基本上並不違反RISC的設計原則,會放進去是因為後來多媒體應用成為微處理機很重要的一項功能,常用的當然會放進去。

2017年6月16日 星期五

淺談古代CPU:副處理器的故事(3)

前情提要:
淺談古代CPU:副處理器的故事(1)
淺談古代CPU:副處理器的故事(2)

Intel在1980年公布了8087,隔年,也就是1981年八月,IBM PC問世,上面特別留了一個空的40 pin IC插座給8087,從此個人電腦終於跟以前那些「大型主機」一樣,擁有浮點運算的能力,也就在這一年,一家叫做Weitek的公司成立。

中文版的Wiki對Weitek的介紹有不少錯誤,首先,所謂「 三位自Intel公司離職的台灣工程師 所共創 」就錯了, 這三位都是HP Lab(Hewlett Packard Central Research Lab)出來的,Chi-Shin Wang( 王繼行 )是1967年台灣大學物理系畢業,後來到Standford念電機博士,台大物理學會在民國56年出版的《時空》第五期有他的文章,他後來在1987年自己開了一家IC設計公司叫IIT,後來改名叫8x8;IIT是頭幾個不靠逆向工程做出387相容浮點副處理器的廠商之一;Edmund Sun (孫燕生) 台灣出生,國立交通大學電子物理系58級畢業,1974年Caltech加州理工學院畢業,後來開了C-Cube,是硬體影像壓縮的先驅,後來還投資設立不少公司;Godfrey Fong (方端) 是總裁兼CEO, 1961-66 上海復旦大學半導體物理碩士,在Weitek之前,1974年他在Fairchild,,1981到HP Lab,這三位都沒待過Intel;另外有紀錄的還有G. Leonard (Len) Baker, Jr.,他是投資董事之一。

真正待過Intel的是這兩位:Stephen (Steve) Farnow,他在1979-1985是Intel DRAM部門的Manager , 1986 年到1990年擔任Weitek的VP;另外就是Everett Roach,他在1987到1991年擔任Weitek的Marketing Manager,在進Weitek之前,他是Intel的Marketing Manager,後來還擔任過Cyrix和NS(美國國家半導體)的VP,現在是Qualcomm的資深PM。

Weitek的創立目的本來是想做電腦繪圖,主要是CAD(電腦輔助設計)的相關系統,但是CAD需要強大的浮點運算, 於是他們從同樣是HP Lab找來一批設計浮點運算電路的專家,比較有名的是Fred Ware,美國專利局還有他們在Weitek時申請的浮點運算專利紀錄;結果系統還沒做,浮點運算器晶片已經先有了生意。

Intel推出8087只比8088晚了一年,剛好趕上IBM PC問世;1983年的80287也只比80286晚了一年;1985年80386問世,1986年Compaq就搶先推出了DeskPro 386,成為第一台相容IBM PC的386個人電腦,可是....80387到了1987年才出來,Weitek就趁這個時候搶先推出了可以搭配Deskpro 386的Weitek 1167,而且宣稱浮點運算速度是387的2-3倍。

1167其實是三顆晶片加上一片很大的電路板,然後再用一塊轉接板插到主機板的插座上,這三顆分別編號是WTL1163、1164和1165,1163是負責介面轉換的晶片,1164是浮點乘法器,1165是ALU;隔年Weitek就把這塊大板子縮成一顆IC,推出同樣用於386系統的3167,再隔一年,也就是1989年,當Intel推出80486DX時,Weitek同時推出Weitek 4167。
雖然同步推出,又號稱是387的幾倍速度,但是這是有代價的;首先,Intel不會這麼佛心的直接讓Weitek使用387的指令集,所以Weitek的副處理器指令集完全是獨立的,這表示軟體要另外修改;第二,Intel也不會讓Weitek使用386和387的專屬介面,怎麼辦呢?


Weitek 的工程師想出了一個絕妙的方法,首先,他們讓Weitek的副處理器同時接上386的位址和資料線(和287/387不同,他們不需要接位址線,但是跟8087很像),讓它變成386記憶體位址上的一個裝置;然後最妙的來了,Weitek把這32條位址線分成兩個部分:高位址A31-A25用來分辨自己跟其他記憶體位址的區別,低的16位元直接當作指令!

這是甚麼意思?意思是Weitek直接把比較高的記憶體位址佔下來,386的定址線是32bit,如果高位址是0xC000(其實就是A31-A25=1100000),Weitek就認定這是給它的指令,就會把低位址的16bit當作指令,另外資料線的32 bit就當作是資料;換句話說,在386來看,一個Weitek FPU 的指令其實就是一個記憶體的存取動作,也就是「mov addr , data」,mov的位址上半部是0xC000,下半部是FPU的指令編碼,而資料就是從暫存器過去。

可是這樣不就表示沒辦法裝4G的記憶體?別傻了,在那個時代,大部分主機板能夠裝到8MB記憶體已經是偷笑了,換句話說整個記憶體定址空間根本絕大部分都是空的,Weitek當然是老實不客氣的佔地為王。

不過Weitek的光輝年代並沒有太久,Intel在486時把浮點運算器整合到CPU裡面,所以速度變快,可是在CPU外面的4167就沒辦法享受到這個好處,另外,像Intel後來推出只有24條定址線的386SX,Weitek的架構就完全沒辦法使用;1989年的4167之後,Weitek就完全沒有在PC上推出新的浮點運算器;在其他平台上也遭遇到同樣的問題:當其他處理器都內建FPU時,外接FPU就毫無優勢可言,雖然到了90年代初開始轉做繪圖晶片,但是這時候為時已晚,1996年被Rockwell收購下市。

雖然Weitek的時代結束,但是這套直接使用CPU位址和資料線的招數倒是活了下來,在486出現後,ISA介面卡的速度和CPU差距越來越大,剛好那時候視訊繪圖對於頻寬的需求日益增加,有人就想到了同樣的招數,直接從CPU的位址和資料線下手,這就是後來的VL(Vesa-Local) Bus。

淺談古代CPU:副處理器的故事(2)


前情提要:淺談古代CPU:副處理器的故事(1)

8087並不是第一顆浮點運算器,甚至也不是第一顆浮點運算「微處理器」;早在1977年,AMD(別驚訝,AMD 1969年成立,只比Intel晚一年)就做了Am9511和Am9512算數運算器,Intel還跟AMD拿了這兩顆的授權,做了8231和8232,當作8080的副處理器,不過這幾顆運算器跟8087不太一樣,對於主CPU來說,它們只是一個單純的輸出入裝置,CPU只是把資料單純的送進去或讀出來,裡面做甚麼對CPU來說是完全無關的。



但是8087就不同了,作為8086的副處理器,8087基本上被看成是CPU的延伸,原則上,8087等於是「擴大」了8086的指令集,我們先花一點篇幅,提一下這個算是基本計算機概論的小常識:指令集。

之前在介紹32bit與4G的時候,我們曾經用黑盒子來概略描述CPU的行為,一個CPU如果當作一個黑盒子,它的行為基本上就是:向外界(基本上就是記憶體)輸出位址,然後存取資料。
現在這個黑盒子要再複雜一點:雖然還是一樣輸出位址給記憶體,從記憶體存取資料,但是對黑盒子內部來說,記憶體存取的東西分成兩種,分別是「指令」和「資料」。

電腦的另外一個名字叫「電子計算機」,顧名思義,電腦是用來計算的,要計算,需要告訴電腦兩件事:第一,做甚麼計算;第二,要被計算的數字;前者稱為指令,後者稱為資料。
很多人都用過算盤或是那種只有數字和加減乘除的掌上型計算機;當你要計算很多數字,例如把幾十個數字各自照公式計算然後平均等等,會怎麼做呢?首先,你要有張紙,上面記錄要計算的數字,以及計算的公式,你看一眼公式,把數字打進計算機或算盤,根據公式計算,然後把計算結果寫回那張紙,完成工作;恭喜你,你做的事情就是現代電腦所做的事情;那張紙就是記憶體,你和算盤或計算機做的事,就是CPU做的事。

電腦與過去那些簡單的計算機最大的不同,在於電腦可以把「如何計算」這個資訊變成一種可以存放在記憶體裡面的資料,這種資料有個特別的名稱叫「指令」,CPU就是從記憶體讀進指令,根據指令再從記憶體裡讀取資料,進行運算,或是把運算結果寫進記憶體,一個CPU可以接受的指令格式,稱為這個CPU的「指令集」。

為什麼要談這麼多基本常識?因為8086「認得」8087的指令;在8086的指令集中,8087的浮點運算指令開頭的第一個byte是11011xxx(xxx表示不固定),這個11011被稱為是ESC code(其實二進位11011就是ASCII code裡面的ESC代碼),8086讀取指令時,看到這個ESC就會知道「不是自己可以處理的指令」(x86的指令格式其實很複雜,如果想要了解全貌,可以參考http://ref.x86asm.net/ 這個網站)。

可是問題來了,8086是讀取了8087的指令後,才會知道「這不是我可以處理的」,問題是8087在8086「外面」,這時候該怎麼辦?再送出去給8087?
8086和8087的工程師想出了一個非常巧妙的方法,簡單的說,8086的接腳除了位址和資料線以外,還有一些額外的訊號線,8086用了一部份訊號線來表示「現在8086的行為」,例如「正在讀取指令」、「正在讀取資料」或是「寫資料到記憶體」等等。

如果各位去看8087的資料,會發現8087的腳位跟8086很像,實際上。8087跟8086大部分訊號線都接在一起,換句話說。8087可以「看」到8086大部分訊號,當8087看到8086正在讀取指令,而且指令的最前面是11011,它就知道這個指令是給它的,不需要另外再送訊號給8087,更妙的是,8086接到8087的指令後,如果這個指令的後段是要8087存取記憶體,8086收到後會把腳位訊號切換為記憶體存取(就是告訴記憶體「我要存取記憶體了」,然後停止動作(這有個專有名詞叫Dummy Read),讓8087接管這些訊號完成存取動作。

不過後來的80287和387就沒有像8087這樣的設計,而是完全等主CPU(286/386)讀進指令,解碼完畢後,再從資料線傳到副處理器,主CPU解碼完畢會再啟動一個I/O動作,286會對I/O位址0xF8、0xFA、以及0xFC輸出資料給287,而386則是0x800000f8 和0x800000fc,手冊裡還會特別聲明,希望程式不要自己去亂動這幾個I/O port位址,不過事實上287和387也不會去真的去看記憶體或是I/O的位址線(事實上它們的接腳裡面沒有位址線)就是了;為什麼不像8087那樣做呢?有個原因是8087的運作方式必須跟8086完全同步,287/387是可以跟主CPU頻率不同,另外一個原因是8087這種設計如果遇到浮點指令本身格式有錯時,8087解碼不出來,8086會一直等待下去,也就是會當掉,一般來說遇到這種情況,應該是CPU解碼後會丟出一個「例外狀況」,附帶一提,如果一個系統沒有裝浮點運算器,卻讀進浮點指令,也是要丟出一個「例外狀況」。

不管怎麼設計,在486之前,因為浮點運算器都是外接的,所以總是會有一些專用的訊號線讓這兩顆IC互相通訊,但是到了486,因為一開始就把浮點運算器和CPU整合成一顆,所以根本就沒有這樣的預留線路,可是後來(1991年中)Intel出了486SX(沒有浮點運算器的便宜版486)後,有些買了486SX的用戶發現沒有浮點運算很不方便要求升級方案時,這個問題就顯得很棘手,怎麼辦呢?Intel的辦法是:出一顆「完整的」486,但是腳位稍微改一下(其實只是多加一根腳),改個名字叫「487 SX」,當這顆晶片裝到主機板預留的腳位時,有一根線會把旁邊那顆486SX「整顆關掉」。

但是這還不是最機車的設計,大概在同一段時間前後(1990-1991),Intel為了應付某些386的用戶需要升級卻不想換主機板的需求,推出了一種稱為RapidCAD的處理器升級方案。

簡單的說, RapidCAD是一種「裝在386腳座上面的486」,晶片本身其實是個拿掉內建cache的486DX,但是對外的腳位完全是386,問題來了,386的浮點運算器80387是外接的,可是486DX的浮點運算器是內建的,怎麼辦呢?

Intel想出來的方法是:多做一顆「假的」387,事實上賣出來的RapidCAD一套有兩顆晶片,一顆稱為RAPIDCAD-1,腳位和386一樣,另外一顆稱為RAPIDCAD-2,腳位跟387一樣;那顆RAPIDCAD-2唯一的用途,是針對某些用來偵測「387存不存在」的腳位(精確一點的說,是FERR這隻腳),送出正確的訊號,讓主機板和處理器都「以為」387在上面,實際上卻是在CPU那邊。

 RapidCAD的整數效能其實並不好,受限於386匯流排的限制以及拿掉了那8K cache,整數效能大概只比同頻率的386高個10%而已,可是浮點運算就很威了,因為跟CPU在同一顆晶片,RapidCAD的浮點運算可以比387高70%,所以這顆其實是為急需浮點運算又不想花錢買整台486系統的386客戶量身打造的,這也是它名字的由來:Rapid"CAD",那個年代,最需要浮點運算的,除了科學研究以外,大概就是電腦輔助設計(CAD)了。


下次我們會來談談「傳說中的」Weitek 3167/4167。

淺談古代CPU:副處理器的故事(3)

淺談古代CPU:副處理器的故事(1)

很久很久以前,有個「寸gate寸金」的時代,一顆IC如果有上萬個電晶體,會被冠上「 超大型積體電路( VLSI)」的神聖稱號;在那個時代之前,一台電腦是一個房間裡的一堆機櫃,「一個CPU」是一塊甚至好幾塊電路板,直到1971年,有家公司叫Intel,把很多元件都塞在一個有2300顆電晶體的IC裡面,做出了史上第一個微處理器4004。

在那個年代,能夠把基本的整數運算功能放到CPU晶片裡,已經是很了不起的成就,不過隨著微處理器的演進,很快的,有很多客戶希望這些新一代的微處理器為中心的系統,可以把本來是「大型電腦」才有的運算能力放進去,問題是,買微處理器的客戶中,有這種需求的可能只有一小部份,可是又不能為這一小部份把這些要耗費大量電晶體的電路做進去,畢竟一片晶片做好以後是沒辦法加裝的,所以唯一的方法,就是另外做一種專用的元件,但是這種元件不能自己獨立運作,必須配合一般的微處理器,這種元件稱為副處理器,不需要的客戶不用多花錢,需要副處理器的客戶可以另外花錢加裝,兩全其美。

首先被規劃做成副處理器功能的是浮點運算,這裡先簡單的解釋一下,所謂的浮點數是一種特別的數字表示法,用來在一定的資料長度內,表示出精確度有限,但是大小範圍很大的數字,在科學運算上常常被用到,在微處理器出現前,很多大型電腦都有浮點運算的能力,不過大家各自有其浮點表示方式;1976年,Intel發表8086之後,就著手設計一款可以跟8086/88搭配的浮點運算器,這個計劃的主持人John Palmer 提議將這顆浮點運算器的數字格式和運算規則公開出來變成一個標準,於是Intel聘請加州大學柏克萊分校的教授William Kahan 作為顧問,向IEEE提交了第一個浮點運算標準,就是現在的IEEE-754,William Kahan教授也因為對於浮點運算標準化的貢獻,得到1989年的Turing Award。

Prof. William "Velvel" Morton Kahan
IEEE-754背後的八卦頗為精采;一開始制定時,主要成員都是當時開始做微處理器的新秀,除了主角Intel以外,Zilog、Motorola也都趕快過來參一腳,另外還有一個做「小型電腦」的 迪吉多(Digital Equipment Corporation,簡稱DEC) ,但是當時幾家大型和超級電腦的巨頭,像是Cray、CDC,根本看不起這些小角色,所以缺席了;雖然草案是Intel提議的,但是當時擁有廣大電腦市場的DEC也不是省油的燈,也根據他們現有的VAX上的浮點運算機制提出了一套草案,兩方以及其他搖擺不定的小廠就這樣開始大亂鬥,從1977年一直吵到1981年,最後剩下一個主要的爭論焦點: Denormal number。

一個「正規」浮點數基本上分成三個部份,首先是1 bit的正負號,接下來兩個部份稱為指數和尾數,指數用來表示數字的大小範圍,尾數則是數字的精確度,如果這兩個欄位都是0,就是「絕對0」,指數部份如果全部是1,表示是無限大或者「不是數字」,其他狀況下,指數基本上最小就是1(0x01),問題來了,有時候計算時會出現這種情況:指數是0,後面尾數卻不是0,這種情況稱為underflow,其實意思是:運算出來的這個數字非常接近0,近到無法用正規的浮點數來表示,這時候該怎麼處理呢?DEC的方法是直接當作0,Intel則是提出了一個很麻煩的方案:把這類數字特別處理,讓它可以表示比正規浮數字更接近0的範圍,這樣的數字就稱為Denormal number。(underflow會有什麼麻煩呢?明明 A>B,但是 A-B卻是0,這樣了解嗎?)

32 bit 浮點數格式


DEC的方法稱為abrupt underflow,而Intel的提案稱為gradual underflow,當時大部份電腦其實都用DEC的方法,因為比較快,但是Intel這方覺得這樣讓0到最小非0浮點數之間的差距太大;雙方各持己見,最後是在1981年,DEC聘請一位馬里蘭大學的錯誤分析專家 G.W. (Pete) Stewart III 教授替他們的方案辯護,但是仔細研究後,Stewart教授認為gradual underflow是較好的方法,勸DEC放棄,爭論總算平息下來,於是在1985年,IEEE-754-1985正式通過,成為浮點運算標準,但是在此之前Intel其實就已經先偷跑, Intel在1980年發表了8087,1983年發表了配合286的80287,這兩款浮點運算器其實還不是完全符合IEEE-754,直到1987年的80387才是第一個真正完全符合IEEE-754-1985標準的浮點運算器。


淺談古代CPU:32bit與4G?(2)

32位元與4GiB的太平歲月從386問世(1985)開始,差不多有10年的時間,直到Pentium Pro問世,Pentium Pro有36條定址線,所以最大可以存取的記憶體範圍是64GiB,這下子又回到了暫存器小於定址線的老問題,更麻煩的是,當初因為8086/286「本來」暫存器就比定址小,所以對應的機制是一開始就設計好的,問題是Pentium Pro的36 bit定址是後來加上去的,如果改動原來的存取機制,前面十年來為386、486寫的眾多程式還要不要活命?所以Intel的決定是:不改動原來程式的定址方式,麻煩的事情交給作業系統搞定。

在介紹Pentium Pro對付4G問題的機制前,我想應該先解釋計算機結構和作業系統都會提到的兩個名詞:「虛擬記憶體位址」和「實體記憶體位址」。

上次提過,如果把CPU當作一個抽象的黑盒子,這個黑盒子對外的行為就只有「存取記憶體」而已,不過實際的狀況下,一顆CPU會輪流跑很多獨立的程式,這時候出現一個問題,對記憶體來說,它只知道對應送來的位址,準備好要存取的資料,但是不會分辨是哪個程式的,換句話說,整個記憶體對這些程式來說是完全一樣的,你的資料我看得到,我的資料你也可以改,這樣不太好。

所以後來有人想出一種解套的方法,簡單的說,就是在黑盒子外面再包上一層黑盒子,把裡層黑盒子放出來的記憶體位址「攔截」下來,經過另外轉換後才變成真正去存取記憶體的位址,對裡層黑盒子執行的程式來說,並不會感覺到有什麼不同,而控制外層黑盒子的程式,可以對每個裡層程式的位址做出適當的轉換,讓它們每個都覺得自己才是記憶體的唯一擁有者。

在這種機制下,每個程式自己所存取的記憶體位址,叫做「虛擬記憶體位址」,而實際上真正的記憶體位址當然就叫做「實體記憶體位址」,負責轉換這兩種位址的,是CPU裡面一個特別的單元,通常稱為Memory Management Unit,記憶體管理單元,簡稱MMU,不是每個CPU都有這種機制,舉個例子,Intel 8088/86就沒有;另外有些CPU是把MMU當作另外一種「副處理器」,要另外安裝或是當作選項;另外,控制MMU的相關程式,通常是作業系統的一部份,所以兩個名詞在作業系統教科書上也看得到。

為什麼要談這個?因為Intel在32位元系統上企圖解決4G問題時,並不是讓程式「直接」就可以定址超過4G記憶體,而是去修改MMU轉換位址的機制;換句話說,對「單一程式」來說,直接可以存取的記憶體定址空間還是只有4GBytes,但是作業系統可以讓多個程式在一個比4G大的記憶體空間上並存,所以虛擬記憶體位址還是只有4GBytes,但是實體記憶體位址擴大了。

Intel有提供兩套擴展記憶體定址的機制,一套叫「實體位址擴展 」(PAE,Physical Address Extension),另外一套叫PSE-36 (36-bit Page Size Extension),因為是淺談,所以詳細的機制就省略不提,有興趣的可以直接找維基百科或是Intel的官方文件;目前的作業系統多半支援PAE,支援PSE-36的比較少;32位元的ARM上面有類似的機制,稱作LPAE(Large Physical Address Extension)。

剛剛的MMU還有後續,回想上面提過的黑盒子,我們一直假設只有CPU會去存取記憶體,其實不一定,聽過「DMA」(Direct Memory Access)和「Bus Mastering」嗎?一般來說,CPU與I/O裝置溝通有兩類方法,一類是「CPU主動」,也就是程式主動去存取I/O裝置,就像是存取記憶體一樣;另外一類是「I/O裝置主動」,這時候I/O裝置就跟CPU一樣,可以主動存取記憶體;不過古早以前,I/O裝置在存取記憶體的時候是當作「實體記憶體定址」來做的,以前是沒有什麼問題,但是當大家引入「虛擬化」的時候就成了大問題,因為虛擬化的時候。這些I/O裝置一樣要被虛擬化,可是它們存取資料的時候就露餡了,所以後來AMD才提出IOMMU這個機制,其實也就是讓I/O裝置跟CPU裡面一樣,存取記憶體時另外有MMU轉換位址。

其實關於32bit和4G還有不少有趣的故事,不過現在大部分都進入64位元時代,只能當作考古,至於64 bit以後還會不會出現類似的狀況,我想有點難,畢竟64位元定址空間並不是32位元的兩倍,而是一個很大的天文數字,不過....誰知道呢?

所以回到一開始那個問題「 在 32 位元 CPU 中,如果單純以 CPU 做記憶體定址時,4GiB 為其極限。 」在x86上,這個敘述的確不能算錯,但是這不是因為「32位元 CPU做不到」,而是Intel和AMD不想做。

淺談古代CPU:32bit與4G?(1)

在 32 位元 CPU 中,如果單純以 CPU 做記憶體定址時,4GiB 為其極限。 」這是某個網站對新處理器「深入分析」文章中出現的敘述。

這句話倒底對不對呢?這就是以下要討論的,其實這些都是很基本的計算機結構常識;首先我們從定義開始:什麼是CPU的「bit數」?CPU的「記憶體定址」又是什麼?

雖然幾十年前對這個定義有不少爭論,不過現在一般來說,一個CPU架構裡的「bit數」,通常從程式觀點來看,指的是「整數暫存器的bit數」,也就是一個整數指令能夠運算的最大位元長度,舉例來說,以前8088/8086和286,整數暫存器最大就是16位元。所以是16位元處理器,80386/486暫存器可以到32位元,所以是32位元處理器,沒有任何問題。

可是「記憶體定址」是另外一回事, 什麼是「記憶體定址」?如果把裡裡外外所有繁瑣的細節都省略,當作一個抽象的個體來看,CPU就是一個可以輸入或輸出(為了方便,以下把輸出和輸入簡稱為讀寫,讀是輸入,寫是輸出)資料的黑盒子,而這個黑盒子從「哪裡」讀寫資料?這個「哪裡」就是記憶體,當然這是個抽象的描述,有些人可能會爭辯還有I/O等等,但是對程式或CPU來說那還是一種「記憶體」。

CPU讀寫資料的大小是有限的,更進一步來說,CPU能夠「從多大的範圍讀寫資料」也是有限制的,回到剛剛的黑盒子,CPU如何決定從「哪裡」存取資料?答案是:CPU會先寫出一串資料,這串資料稱為「位址(address)」,這串資料是有大小限制的,如果是32bit,表示CPU只能從2^32這麼大的範圍內選一段讀寫,一段的基本單位通常是byte(當然,也會有例外),古早的CPU通常會特別有一組訊號線來表示這串資料,這組訊號叫「定址線」,所以32條定址線,基本單位byte,表示這顆CPU只能從2^32 = 4 giga byte = 4GiB大小的空間中讀寫資料,也就是說記憶體定址空間是 4 GiB。

所以32位元CPU的記憶體定址就是4 GiB?

回答這個問題之前,請先容我反問一下,如果上面的敘述成立,請問16位元的CPU,記憶體定址應該是多大?2^16 = 64K byte?

別傻了,就拿剛剛舉過的例子,Intel 8088/8086有20條定址線,80286有24條定址線,所以一個是1MB,一個是16MB,隨便哪一個都超過64K。

問題出在哪裡?問題在於,整數暫存器的大小,跟記憶體定址線的大小,這兩個本來就是互相獨立的參數,上面的8086和286就是個例子,只是x86進入32位元時代後,剛好386、486到Pentium都是位元數等於定址線數,也就是暫存器32位元,定址線也剛好是32位元,如此而已。

問題又來了,如果一顆CPU的整數運算大小比定址線還小,那程式怎麼存取記憶體?這也是個好問題,答案是:看指令集怎麼規定;拿8086來說,它有幾個特別的暫存器,牽涉到記憶體存取的指令時,CPU真正計算位址的方法是:把這些暫存器的值,跟你的指令裡面指定位址的值拿來一起計算,最後產生的才是20位元的記憶體位址;到了286時比較複雜,某個暫存器會指到某張表,表裡面的數字跟暫存器的數字一起計算等等等等,其他處理器可能有其他方法,但是總而言之言而總之,有太多太多方法可以讓程式在存取記憶體時,可以不用受限於一個整數暫存器的大小。

所以一切的謎底都解開了嗎?也不盡然如此,在386之前,這種不太直觀的記憶體存取機制,對程式設計師來說其實很麻煩,所以從386開始,暫存器和定址線就一樣是32位元,程式可以擺脫那些麻煩的機制,就這樣過了很長的一段太平日子,因為太平日子過太久,所以大家都忘記了過去那段苦日子,以為32位元就等於4G記憶體定址。

不過太平日子其實也是會過去的,故事還沒完,我們下期待續。