1 前言
??本文是基于《編碼》融欧、《穿越計算機的迷霧》兩部著作進行讀后整理的記錄性博客他炊。對書中較為重要的內容進行歸納整理進行二次創(chuàng)作,略去了繁瑣的講述細節(jié)咧擂,力求簡明扼要。
編碼:一種由若干符號和規(guī)則組成的系統(tǒng)檀蹋,用來向計算機表述指令松申。
2 正文
2.1 累加器
??在前面的文章中我們介紹了加法器,下面是其結構圖,這個版本的加法器包括一個 8 位的鎖存器贸桶,用于對 8 個開關的輸入數據進行迭代求和舅逸。
??上圖中 8 位鎖存器利用觸發(fā)器來保存 8 位數據。使用這個設備時皇筛,首先需要按下清零開關使鎖存器中的內容全部都變?yōu)?0琉历,然后通過開關輸入第一個數。加法器只是簡單地將這個數字和鎖存器輸出的 0 進行求和设联,因此相加的結果與原先輸入的數字是一樣的善已。按下相加開關可以把這個數保存在鎖存器中,最后會點亮某些燈泡以顯示它±肜現在通過開關輸入第二個數换团,加法器把它與已經存放在鎖存器中的第一個數相加。再次按下相加開關宫蛆,就可以把相加的結果存入鎖存器中艘包,并通過燈泡顯示這個結果。通過這種方式耀盗,可以把一串數相加并顯示運行結果想虎。
??很顯然,上面的加法器存在著一個很大的缺陷:假如要把 100 個二進制數加起來叛拷,你必須端坐于加法器前舌厨,并且耐心地輸入所有的數并累加起來。但是當你終于完成時忿薇,卻發(fā)現其中有兩個數輸錯了裙椭,而你只能重復一遍所有的工作。
??基于上述介紹本文將設計一種新的電路設備署浩,用來實現累加揉燃,這種用來累加多個數的鎖存器稱做累加器(accumulator)。
??在前面的內容中我們利用觸發(fā)器構造了一個 64KB 的 RAM 陣列筋栋,這里我們添加一個控制面板連接到電路幫助我們操作炊汤。如下圖所示,即為 64KB RAM 陣列結構圖:
??結合我們前面文章中介紹的 RAM 陣列弊攘,如果把這 100 個需要進行累加的二進制數輸入到 RAM 陣列中而不是直接輸入到加法器中抢腐,一旦需要修改一些數據,我們的工作將會變得容易得多肴颊。
??因此氓栈,現在我們要做的是如何把 RAM 陣列和累加器連接起來。這里我們用 RAM 陣列的輸出信號可以替代加法器的開關婿着,用一個 16 位的計數器控制 RAM 陣列的地址信號授瘦。在這個電路中醋界,RAM 陣列的數據輸入信號和寫操作端信號可以省去。
??要使用它提完,首先要閉合清零開關形纺,這樣做的目的是,清除鎖存器中的內容并把 16 位計數器的輸出置為 0000h徒欣,然后閉合 RAM 控制面板的控制端開關≈鹧現在你可以從地址 0000h 開始輸入一組你想要相加的 8 位數。如果有 100 個數打肝,那么它們將被存放在 0000h~0063h 的地址空間中脂新。然后閉合 RAM 控制面板的控制端開關(這樣控制面板就不再控制 RAM 陣列了),同時斷開清零開關粗梭。
數值后面加上字母 h 代表這個數是一個十六進制數
??我們來分析一下它的工作過程:當清零開關第一次斷開時争便,RAM 陣列的地址輸入是 0000h。RAM 陣列的該地址中存放的 8 位數值是加法器的輸入數據断医。加法器的另一個輸入數據為 00h滞乙,因為此時鎖存器也已經清零了振蕩器提供的時鐘信號——一個可以在 0,1 之間快速切換的信號鉴嗤。清零開關斷開后斩启,當時鐘信號由 0 跳變?yōu)?1 時,將有兩件事同時發(fā)生:鎖存器保存加法器的計算結果醉锅,同時 16 位計數器增 1兔簇,指向 RAM 陣列的下一個地址單元。清零開關斷開之后硬耍,時鐘信號第一次從 0 跳變?yōu)?1 時男韧,鎖存器就將第一個數值保存下來,同時計數器增加為 0001h默垄;當時鐘發(fā)生第二次跳變時,鎖存器保存之前兩個數的求和結果甚纲,同時計數器增加為 0002h口锭;按這種方式往復操作。
??這個電路存在一些明顯的缺陷:我們無法停止計算介杆,而且在 RAM 陣列中鹃操,剩余部分存儲的數都是 00h,當計數器達到 FFFFh時春哨,它會重新回滾到 0000h荆隘,這時加法器會再一次把所有的數累加到已經計算出來的結果中去。當然還有的問題是赴背,它只能做加法運算椰拒,并且只能做 8 位數的加法晶渠。
??概括來說,我們需要一種 “萬能機”燃观,它可以很方便地對褒脯;兩個數,10 個數甚至 100 個數求和缆毁,并且所有計算結果都可以很方便地使用番川。
??我們可以去掉與鎖存器連接的燈泡,取而代之的是把鎖存器的輸出端連接到 RAM 陣列的數據輸入端脊框,這樣就可以把計算結果寫回到 RAM 陣列中去颁督,如下圖所示。
??上圖中略去了自動加法器的其他部分浇雹,其中包括振蕩器和清零開關沉御,這是因為我們不再需要特別標注計數器和鎖存器的清零及時鐘輸入。此外箫爷,我們利用了 RAM 陣列的數據輸入嚷节,同時也需要一個方法去控制 RAM 的寫入信號。
把關注點集中到我們需要解決的問題虎锚,我們現在想要配置一個自動加法機硫痰,使它不僅僅可以對一組數字做累加運算,還希望它能夠自主確定要累加多少個數字窜护,而且記住在 RAM 中存放了多少個計算結果效斑。但不難發(fā)現,針對我們想要實現的功能柱徙,結合上述電路缓屠,我們需要對各種操作類似編寫程序一樣進行明確指明,什么時候相加护侮,什么時候停止計算敌完,結果作何處理。僅憑加法機自身很難達到 “自動”羊初,其實生活中我們接觸的電腦也是如此滨溉,根據程序指令執(zhí)行相應的操作。
??例如长赞,我們需要先對三個數進行求和晦攒,然后再對兩個數進行求和,最后再對三個數進行求和得哆。想象一下脯颜,我們可以把這些數保存在 RAM 陣列中以 0000h 開始的一組空間中,這些數存儲在 RAM 陣列中的具體形式如下圖所示贩据。
在本文以及后續(xù)內容中栋操,將用這樣的形式表示一小段存儲器闸餐,方格表示的是存儲器的內容,地址標記在方格的左邊讼庇,
??我們希望自動加法器能夠做四個事:進行加法操作绎巨,首先它要把一個字節(jié)從存儲器中傳送到累加器中,這個操作稱為加載(Load)蠕啄。第二個操作把存儲器中的一個字節(jié)加(Add)到累加器的內容中去场勤。第三個操作把累加器中的計算結果取出并存放到存儲器中。另外我們需要用一個方法令自動加法器停(Halt)下來歼跟。
??我們借助具體的例子詳細介紹這一過程和媳,以上文提到的自動加法器所做的運算為例來說明。
(1)把 0000h 地址處的內容加載到累加器哈街。
(2)把 0001h 地址處的內容加到累加器中留瞳。
(3)把 0002h 地址處的內容加到累加器中。
(4)把累加器中的內容存儲到 0003h 地址處骚秦。
(5)把 0004h 地址處的內容加載到累加器她倘。
(6)把 0005h 地址處的內容加到累加器。
(7)把累加器中的內容存儲到 0006h 地址處作箍。
(8)把 0007h 地址處的內容加載到累加器硬梁。
(9)把 0008h 地址處的內容加到累加器。
(10)把 0009h 地址處的內容加到累加器胞得。
(11)把累加器中的內容存儲到 000Ah 地址處荧止。
(12)令自動加法器停止工作。
??我們還希望自動加法器能方便地停下來阶剑,以便于查看 RAM 陣列中存放的值跃巡。
??該如何來完成這些工作呢?能不能僅僅簡單地向 RAM 陣列中輸入一組數牧愁,然后期待自動加法器正確地完成所有工作呢素邪?答案是否定的。對于 RAM 陣列中的每一個數猪半,我們還需要用一些數字代碼來標識加法器要做的每一項工作:加載娘香、相加、保存和終止办龄。
??也許存放這些代碼的最簡單的方法是把它們存放在一個獨立的 RAM 陣列中。這個 RAM 應該和第一個 RAM 同時被訪問淋昭。但是這個 RAM 中存放的是不需要求和的數俐填,而是一些數字代碼,用來標記自動加法器對第一個 RAM 中指定地址要做的一種操作翔忽。這兩個 RAM 可以分別被標記為 “數據”(第一個 RAM 陣列)和 “代碼”(第二個 RAM 陣列)英融。其結構如下圖所示盏檐。
??我們已經清楚地認識到新的自動加法器能夠把數據求和的結果寫入到第一個 RAM 陣列(標記為 “數據”),而新的 RAM 陣列(標記為 “代碼”)則只能通過控制面板寫入驶悟。
??我們需要四個代碼來標記新的自動加法器需要做的四個操作胡野,這些代碼可以任意指定。如下所示的是一種方案痕鳍。
??如下圖所示硫豆,比較一下該 RAM 陣列與存放累加數據的 RAM 陣列中的內容,不難發(fā)現笼呆,代碼 RAM 陣列中存放的每一個代碼都對應著數據 RAM 中要被加載或者加到累加器中的數熊响,或者對應需要存回到數據 RAM 中的某個數。以這種方式使用的數字代碼常常被稱為指令碼(instruction code)或操作碼(operation code诗赌,opcode)汗茄。它們指示電路要執(zhí)行的某種操作。
??如前所述铭若,最初的自動加法器的 8 位鎖存器的輸出要作為數據 RAM 陣列的輸入洪碳,這就是 Save 指令的功能。還需要做另一個改變:以前 8 位加法器的輸出是 8 位鎖存器的輸入叼屠,但現在為了執(zhí)行 Load 指令瞳腌,數據 RAM 陣列的輸出有時也要作為 8 位鎖存器的輸入,這種新的變化需要一個 2-1 選擇器來實現环鲤。改進后的自動加法器如下圖所示纯趋。
16 位的計數器為兩個 RAM 陣列提供地址輸入。通常冷离,數據 RAM 陣列的輸出傳入到 8 位加法器執(zhí)行加操作吵冒。8 位鎖存器的輸入可以是數據 RAM 陣列的輸出(當執(zhí)行 Load 指令時),也可以是加法器的輸出(當執(zhí)行 Add 指令時)西剥,這種情況下就需要 2-1 選擇器痹栖。通常,鎖存器電路的輸出又流回到加法器中瞭空,但是當執(zhí)行 Save 指令時撑刺,它就成為了數據 RAM 陣列的輸入數據付翁。
??上圖缺少的是控制所有這些組件的信號,它們統(tǒng)稱為控制信號,包括 16 位計數器的 “時鐘” 輸入和 “清零” 輸入痰洒,8 位鎖存器的 “時鐘” 輸入和 “清零” 輸入,數據 RAM 陣列的 “寫”(W)輸入回俐,2-1 選擇器的 “選擇”(S)輸入吹艇。其中的一些信號很明顯是基于代碼 RAM 陣列的輸出,例如钮蛛,如果代碼 RAM 陣列輸出是 Load 指令鞭缭,那么 2-1 選擇器的 “選擇” 輸入必須是 0(即選擇數據 RAM 的輸出)剖膳。只有當操作碼是指令 Store 時,數據 RAM 陣列的 “寫”(W)輸入必須是 1岭辣。這些控制信號可以通過邏輯門的各種組合來實現吱晒。
在前面我們將電路修改為兩個 RAM 陣列的形式,其中一個 RAM 陣列對應我們想要執(zhí)行的操作沦童。觀察上述電路不難發(fā)現我們無法得知這些指令操作是怎么控制電路的仑濒。但我們細想其實可以通過邏輯電路將操作對應的 RAM 陣列的輸出與電路中的各個控制位進行連接,從而實現不同輸出控制電路實現相應的操作搞动。這里暫且忽略這些繁瑣的控制信號躏精,當作已經連接完成,代碼對應相關操作已經實現鹦肿。
??利用最少的附加硬件和一些新增的操作碼矗烛,可以讓這個電路從累加器中減去一個數。第 1 步是向操作碼表增加一些代碼箩溃。
??對于 Add 和 Subtract 的代碼瞭吃,其區(qū)別僅在于最低有效位,我們稱該位為 C0(20h 和 21h)涣旨。如果操作碼為 21h歪架,除了數據 RAM 陣列的數據傳入加法器之前要取反,并且加法器進位輸入置 1 之外霹陡,電路所做的操作與執(zhí)行 Add 指令所做的操作相同和蚪。在這個增加了一個反相器的改進電路中,C0 信號可以完成這兩項任務烹棉。改進后的電路結構圖如下攒霹。
加上反相器實現減法操作
??假設現在要把 56h 和 2Ah 相加,然后再從中減去 38h浆洗,可以按照下圖中兩個 RAM 陣列中的代碼(操作碼)和數據(操作數)完成該運算催束。
??Load 操作完成之后,累加器中的值更新為 56h伏社,加法操作完成后累加器中的值為 56h 與 2Ah 的和抠刺,即 80h。Subtract 操作使數據 RAM 陣列的下一個值(38h)按位取反摘昌,得到 C7h速妖。當加法器的進位輸入置1時,取反得到 C7h聪黎,然后使其與 80h 相加罕容,最后的結果是 48h。
還有一個一直沒有找到合適的解決辦法的問題:加法器及連接到它的所有設備的寬度只有 8 位。對于較多位數的運算杀赢,寬度無法滿足需求。當然我們可以選擇將兩個 8 位加法器連接在一起湘纵,構成一個 16 位的設備脂崔。但這里我們介紹另一種代價更小的解決方法。
??例如我們想把下列兩個 16 位的數相加梧喷,比如:
??這種 16 位的加法可以先單獨處理最右邊的字節(jié)(通常稱之為低字節(jié)):
??然后再計算最左邊的字節(jié)砌左,即高字節(jié)的和:
??得到相同的結果 99D7h。因此铺敌,如果我們把兩個 16 位的數用這種方式保存在存儲器中汇歹,就像下面這樣:
??運算結果 D7h 將被保存到地址 0002h,而結果 99h 將被保存到地址 0005h偿凭。
??當然并非所有的情況都是如此處理产弹,對于分開計算后產生進位的加法該怎么做呢?例如 76ABh + 236Ch弯囊,對兩個數的低字節(jié)求和時將會產生一個進位痰哨。產生的進位必須與兩個數的高字節(jié)的和再相加。
??因此為了保證計算的正確性匾嘱,我們必須要處理分開計算后的進位情況斤斧。我們需要做的僅僅是在第一步運算時保存低字節(jié)數運算的進位輸出,并把它作為下一步高字節(jié)數運算的進位輸入霎烙。
??如何保存 1 位呢撬讽?我們可以采用 1 位鎖存器,該鎖存器應該被稱為進位鎖存器悬垃。為了使用進位鎖存器游昼,還需要另一個操作碼,我們稱之為 “進位加法”盗忱。
??如果要對兩個 16 位的數進行加法運算酱床,我們仍然使用常規(guī)的 Add 指令對兩個低字節(jié)數進行加法運算。加法器的進位輸入是 0趟佃,而其進位輸出被鎖存到進位鎖存器中扇谣。當把兩個高字節(jié)數相加時,要使用新的 Add with Carry 指令闲昭。在這種情況下罐寨,兩個數相加時要用進位鎖存器的輸出作為加法器的進位輸入。
??如果要進行 16 位數的減法運算序矩,則還需要一個新的指令鸯绿,稱為 “借位減法”。通常,Subtract 指令需要將減數取反并且把加法器的進位輸入置 1瓶蝴。進位輸出通常不是 1毒返,因此應該被忽略。但對 16 位數進行減法運算時舷手,進位輸出應該保存在進位鎖存器中拧簸。在進行第二步的高字節(jié)減法運算時,鎖存器保存的結果應該作為加法器的進位輸入男窟。
??在加入了 Add with Carry 和 Subtract and Borrow 之后盆赤,目前我們已經有了 7 個操作碼,如下表所示歉眷。
??增加了兩個新的操作碼之后牺六,我們已經極大地擴展了加法器的功能,它不再局限于 8 位數的加法運算汗捡。通過執(zhí)行進位加法操作淑际,可以對 16 位數、24 位數凉唐、32位數庸追,甚至更多位的數進行加法運算。假如要進行兩個 32 位數 7A892BCDh 和 65A872FFh 的加法運算台囱,我們僅需要 1 條 Add 指令和 3 條 Add with Carry 指令淡溯,如下圖所示。
??當然簿训,把這些數依次輸入存儲器并不是最好的做法咱娶。因為你不但要使用開關來輸入這些數,而且保存這些數的存儲單元的地址也不是連續(xù)的强品。除此之外膘侮,當前設計的自動加法器不允許在隨后的計算中重復使用前面的計算結果。
產生上述情況的原因就在于我們構造的自動加法器具有如下的特性:它的代碼存儲器和數據存儲器是同步的的榛、順序的琼了,并且都從 0000h 開始尋址。代碼存儲器中的每一條指令對應數據存儲器中相同地址的存儲單元夫晌。一旦執(zhí)行了一條 Store 指令雕薪,相應的,就會有一個數被保存到數據存儲器中晓淀,而這個數將不能重新加載到累加器所袁。
3 小結
??編碼:自動加法器(一)篇以前文介紹的 RAM 陣列為基礎,結合 RAM 陣列設計出能夠進行自動計算的加法器凶掰,但目前的加法器還存在很多問題燥爷,需要后續(xù)再進行改進蜈亩。為了精簡內容刪減了部分較為詳細的書寫,僅作為整理總結前翎。