為什麼8086CPU不支持將數據直接送入段寄存器的操作?
最近在學習彙編語言,書上對於這個問題的答覆是屬於硬體設計的問題,但是沒有具體講明白,所以求問各位具體的設計是什麼樣的,或者為什麼8086CPU要這麼設計,多謝各位 : )
註:僅限x86-16bit環境討論
我個人覺得,就是編碼太麻煩,opcode不太夠用,指令太長。
彙編指令由前綴、opcode、操作對象組成,具體的來說,如下圖:
000 AX AL
001 CX CL
010 DX DL
011 BX BL
100 SP AH
101 BP CH
110 SI DH
111 DI BH
另外有一個bit專門判斷選擇的是8bit還是16bit寄存器,編碼表上並無段寄存器存在。
為什麼當時不把段寄存器也放進去?
原因是放進去的話,指令就不好編碼了。
因為MOV操作的寄存器有兩個,其中的一個還可能是內存,還可能是寄存器組合,這樣的話,2*3bit用於描述寄存器,剩下2個bit用於描述是內存還是別的東西,這樣算下來,一個位元組勉強夠用。而如果把段寄存器加上,就需要4個bit編碼一個寄存器,僅僅描述兩個寄存器就需要一個位元組,加上其它的修飾,MOV指令長度就需要整體增加一個位元組,這對於過去的計算機來說,開銷無疑是非常巨大的,過去計算機內存都是以KB計算的,每個MOV指令增加一個位元組,在設計者看來是無法接受的。早期的彙編指令設計原則之一就是盡量少佔存儲空間,因為當時的存儲設備實在是太貴了。
下圖中就是具體MOV指令的編碼(不含段寄存器)情況:
同時,Intel的指令是慢慢發展起來的,並且保證了指令的二進位兼容,Intel早期為了提高指令的速度,還對一部分常用的指令進行了優化,上圖中紅框里的三個指令就是針對常用指令進行的優化。優化的目的是縮短指令長度(MOV reg,imm縮短1位元組,MOV AX,[mem]縮短1位元組)。
如果把段寄存器都加上,MOV指令最短長度就需要3位元組,並且1011 w reg這種編碼就不夠用了(因為加上段寄存器的話reg就需要4bit)。
而且,如果MOV里加了,INC/DEC要不要加?XCHG要不要加?如果都加上,INC/DEC用單位元組也不好編碼了,XCHG用單位元組也不好編碼了。
實際中,操作段寄存器的MOV指令,用了另外的opcode編碼:
那麼為什麼這裡不把立即數加上呢?因為這裡頭,如果加上立即數的話,需要再佔用一個opcode的值。而MOV指令本身已經使用了28個opcode的數值了,單位元組的opcode一共只有256個,考慮到未來的擴展需要,肯定不能把256個值都用了,所以單位元組的opcode能省就省,考慮到修改段寄存器並不是很常見的操作,所以就省了這個opcode
其實到了32位時代,如果徹底放棄兼容性,重新設計彙編編碼的話,完全可以把立即數-&>段寄存器的指令加上,但因為Intel的文化里,兼容性是非常重要的,所以Intel不放棄兼容性,指令只要這樣一代代傳下來。
Intel當年為了讓指令儘可能的段,用了很多單位元組指令:
前綴類指令:9個
XCHG:8個標誌位相關:12個INC/DEC:16個PUSH/POP相關:27個IN/OUT:8個再加上跳轉類的用掉的將近40個opcode,各種算數運算用掉的30多個,基本上單位元組的編碼已經用的差不多了。
所以,為了讓指令更緊湊,只能把常用的寄存器的常用功能編碼,不常用的只能以後再說。
部分單位元組指令的編碼構成(reg代表通用寄存器,sreg2代表段寄存器):
答案可能是, Intel希望程序員在使用段寄存器時, 是事先在內存里寫好, 再讀入到段寄存器里.
看上去是一句廢話.
逐步分析吧
題主的問題可以轉化為: 為什麼Intel 8086指令集里沒有 MOV Seg Imm; 這種指令?1978年的8086處理器定義了很多種類型的mov.8086 8086-2 8086-1 第26頁 ]關於段寄存器的是下面兩個:MOV Seg, Reg/Mem;MOV Reg/Mem, Seg;他們的Opcode分別是8E和8C, 是兩個專用指令. 由於Reg/Mem欄位可以是寄存器, 或者內存, 所以如果想要給Segment Register賦值, 可以使用:MOV DS, AX;MOV ES, [0x5000];
同理, 如果Intel願意指定一種MOV Seg Imm指令, 是很輕鬆的. 只要定義某一個Opcode, 然後仿照上圖第二種Move, Immediate to Register (0xC6/0xC7)的格式, 定義指令就行了. 但是Intel 沒有做. 這裡問題可以再轉化為: 為什麼不引入這樣一個Opcode?
(需要額外解釋, Opcode會定義後面的ModR/M中Register的類型, 無論是General還是Segment Register, 都不會有給 ModR/M編碼添加額外的bit的代價.)是因為Opcode Map資源已經被消耗光了?
不是. 在1978年, Intel引入Segment概念時, Opcode是8bit, 總共有256種組合. 8086上有72條彙編指令, 佔據234種組合. 當時有22個Opcode是沒有使用的(其中有2個, 0xD6, 0xF1 直到今天都沒有被使用) Intel當初是完全有餘地設計其他的Opcode的.是因為硬體電路結構? 工程上的原因?
不是. 年代久遠, 僅能推測. 下面是8086的結構圖. 可以看到General Registers (左上), 和Segment Registers (右中) 都是與數據匯流排互聯的 (圖左ALU D). 是雙向可讀寫的. 來自Instruction Queue (右下) 的Immediate是有數據通路指向Segment Registers的. 在工程上也許需要些額外的電路, 但是把數據從Immediate傳遞到Segment的路徑是完全暢通的.[ 引用: 8086 Architecture ]有任何其他指令能把Immediate參數移進Segment Register里?
有. 比如 Jmp Seg:Offset;有除了Mov/Jmp之外的方法去改寫Segment Registers?
有. 8086指令集有兩個Opcode - LES(0xC4)/LDS(0xC5), 它們的操作數是[mem], 用來地址中的Segment值載入進ES/DS寄存器中. 可見Intel相信Segment的值是存放在數據段里, 因此設計了這兩個指令.到這裡只是為了說明, Intel 8086在工程上完全支持 Mov Seg, Imm; 但卻從最初就沒有設計這條指令. 想必目的是盡量避免用立即數的形式把Segment的值寫在代碼里, 而是讓它保存在數據段中, 通過load操作獲取. 這種行為更像是編程習慣, 也許是因為把Segment放在數據段中有益於維護, 這也僅是對於38年前代碼風格的猜想.
到了80286時代. Intel引入了保護模式, 段寄存器中變成了段選擇器, 包含3bit的控制位, 不再是純粹的地址. 不太適合直接用立即數賦值. 因此題主的問題, 從1978-1982年這個時期的角度去推測才更有意義.[ 引用: 64-ia-32-architectures-software-developer-manual Vol3. Chapter3. Figure 3-6 ]已邀請 @藍色大神,stack overflow上的帖子講了,但也只是揣測,沒有實證。見assembly - 8086- why can"t we move an immediate data into segment register?期待有人解答。
因為 8086 處理器的指令操作數並不是和彙編的助記符一一對應的。機器語言里並沒有一個操作數和 MOV 對應,然後接受兩個參數。
MOV 之後的 source 和 target 都會影響操作數的表達。不同的操作數在彙編里可以都用 MOV 來表示。操作數沒有定義從立即數到段寄存器這種組合。如果是不用兼容的,那些公司幾乎全部破產收購了,只有IBM還有一點
推薦閱讀:
※CPU 的外頻是什麼,所謂的倍頻是如何實現的?
※如何評價國產處理器——飛騰公司的「火星」?
※處理器是如何實現對程序的fetch的?分支預測是在前端解碼後就進行的嗎?
※CPU 的溫度有沒有一個上限值?
※為什麼 CPU 不能直接對內存中的數據進行運算?