Intel 8086

Intel 8086（亦稱之為iAPX 86）是由英特爾公司於1976年初開始設計，1978年年中發表的Intel第一款16位元微處理器。隨後於1979年，又推出了Intel 8088，它在晶片的外部連接椎使用8位資料匯流排（允許使用更廉價和更少積體電路）的支援，成為8086的一個低成本的簡化產品，並用在IBM PC的原始設計中的處理器（包括廣為人所知的IBM PC XT）中而被人知曉。

8086是Intel最成功的x86架構處理器系列的開端。

## 歷史

### 背景

1972年，Intel發布了8008微處理器——世界上第一款8位微處理器。8008微處理器有18個引腳，其中地址總線使用了14個引腳，且與8位數據總線復用引腳。指令集源自是Datapoint公司為電腦的CRT-鍵盤終端設計的但相當通用的指令集。當時英特爾還是一家生產DRAM為主業的公司，缺乏這方面的技術儲備。

1974年，Intel發布了8080微處理器，被公認是第一款真正可用的微處理器。8080的晶片封裝採用40個引腳，其中8個數據總線引腳、16個地址總線引腳都是專用的，因此數據總線與地址總線可以並行工作。8080的擴展後的指令集在原始碼級別上向前兼容8008指令集。

1975年，Intel發布了第三款8位微處理器——8085。採用了新的製造工藝，簡化了輸入電壓引腳的數量。同一時期還有Motorola 6800（1974）、Microchip PIC16X（1975）、MOS Technology 6502（1975）、Zilog Z80（1976）及Motorola 6809（1978）等8位微處理器。在8位微處理器市場競爭中Z80最為成功。

### 第一種x86設計

8086項目起始於1976年5月，是英特爾公司當時更為看重的16位元的Intel iAPX 432微處理器的備份項目。8086一方面要與Motorola, Zilog, National Semiconductor等公司的16位元、32位元微處理器競爭市佔率，另一方面也是對Zilog Z80在8位元位微處理器市場上的成功的回擊。由於採用了與8085微處理器近似的微體系結構與物理實現工藝製程，8086項目進展相當快。

8086微處理器被設計為在彙編源程序上向前兼容8008, 8080, 8085等微處理器。指令集與編程模式是基於8080微處理器，但指令集做了擴充以完全支援16位元運算。

新增加的指令包括：完全支持有符號整數、段基址+偏移量尋址、類似於Z80的自重複操作、直接支持嵌套的ALGOL類語言如Pascal或PL/M、微碼實現的乘法除法指令、以及更好支持與協處理器（8087或8089）和多處理器系統的總線結構。

第一版的指令集與高層的體系結構的設計僅用了3個月。在沒有CAD工具的時代，4名工程師與12名布線繪圖員平行工作，用了2年多的時間才把8086的高層設計實現為可運行測試的產品。這在當時算是很快的研發速度。

8086是隨機邏輯與微碼的混合實現，使用了大約20,000個電晶體（算上所有的ROM與可編程邏輯陣列為29,000個電晶體）。晶片面積為33 mm²，製程為3.2 μm.

8086體系結構由Stephen P. Morse設計，並在最後定案時得到Bruce Ravenel（8087體系結構設計者）的幫助。邏輯設計者是以Jim McKevitt與John Bayliss為首的硬體開發工程師團隊。項目經理William Pohlman。迄今8086的指令集仍然是個人電腦與伺服器的基本指令集。

## 細節

Intel 8086寄存器

19｜18｜17｜16｜15｜14｜13｜12｜11｜10｜09｜08｜07｜06｜05｜04｜03｜02｜01｜00｜（比特位置）主寄存器｜AH｜AL｜AX（累加器）｜BH｜BL｜BX（基址寄存器）｜CH｜CL｜CX（計數寄存器）｜DH｜DL｜DX（數據寄存器）變址寄存器 0 0 0 0｜SI｜源變址寄存器 0 0 0 0｜DI｜目的變址寄存器 0 0 0 0｜BP｜基址指針寄存器 0 0 0 0｜SP｜堆棧指針寄存器 程序計數器 0 0 0 0｜IP｜指令指針 段寄存器 CS｜0 0 0 0｜代碼段寄存器 DS｜0 0 0 0｜數據段寄存器 ES｜0 0 0 0｜附加段寄存器 SS｜0 0 0 0｜堆棧段寄存器 狀態寄存器｜O｜D｜I｜T｜S｜Z｜A｜P｜C｜標誌寄存器

### 總線與操作

所有的內部寄存器、內部及外部數據總線都是16位寬，因此是完全的16位微處理器。20位外部地址總線，因此物理定址空間為1MiB (即220 = 1,048,576).由於內部寄存器都是16位，對1M地址空間尋址時採取了段尋址方式。8086的封裝採用40引腳的雙列直插（dual in-line），數據總線與地址總線復用了前16個引腳。16位的I/O地址，因此獨立的I/O尋址空間為64KiB的虛擬定址空間 (即216 = 65,536).由於8086內部的地址寄存器是16 位寬，因而最大線性尋址空間為64 KiB.使用超過64 KiB記憶體空間的程序設計時，需要調整段寄存器（segment registers）。直到32位的80386出現之前，8086的這種段尋址相當不便.

8086晶片封裝的8個控制引腳在min或max下有不同功能。"min"模式是使用單處理器的小型計算機系統，"max"模式是使用多處理器的中到大型計算機系統。

### 寄存器與指令

8086有8個16比特的寄存器，包括棧寄存器SP與BP，但不包括指令寄存器IP、控制寄存器FLAGS以及四個段寄存器。AX, BX, CX, DX,這四個寄存器可以按照字節訪問；但BP, SI, DI, SP,這四個地址寄存器只能按照16位寬訪問。

8086以8080和8085（它與8080有彙編語言上的原始碼相容性）的設計為基礎，擁有類似的暫存器集合，但是擴充為16位元。總線接口單元（Bus Interface Unit）透過6位元組預存（prefetch）的貯列（queue）將指令送給執行單元（Execution Unit），所以取指令和執行是同步的－一種流水線的原始形式（8086指令長度變化從1到6位元組）。

8086有四個完全一樣的16位元暫存器，但也能夠當作八個8位元暫存器來存取；以及四個16位元索引暫存器（包含堆棧索引）。數據暫存器通常由指令隱含地使用，針對暫存值需要複雜的暫存器配置。它提供64K 8位元的輸出輸入（或32K 16位元）埠，以及固定的向量中斷。大部分的指令只能夠存取一個記憶體位址，所以其中一個運算子必須是一個暫存器。運算結果會儲存在運算子中的一個。

8086有4個記憶體區段（segment）暫存器，可以通過索引暫存器來設定。區段暫存器可以讓CPU一種當時是全新的方式存取多達1MB之記憶體。在現今有區段的處理器中，8086把區段暫存器左移4位元然後把它加上去尋址。這通常被認為是一個不太好的設計，因為這麼做會讓各區段有重疊。儘管這樣對於彙編語言而言會顯得有用——因為可以充分控制區段，但同時卻使高級語言中的指針（像是C程式語言）使用變得困難。它降低了指針的高效率，且有可能產生兩個指向同一個地方的指標擁有不同的位址。更壞的是，這種方式產生要讓記憶體擴充到大於1MB的困難。而80286的尋址方式改變讓記憶體擴充較有效率。

由於8位機（如Intel 8008）時代沿襲下來的緊湊編碼，大多數指令雖然有兩個源操作數（operands）及一個輸出結果，但單條指令至多使用兩個地址，因此運算結果被存入一個源操作數中。且最多只能有一個記憶體操作數，另外的操作數是寄存器或立即數。記憶體操作數也可以用於存儲指令的輸出結果。8位微處理器的指令集不能把輸出結果直接保存在記憶體操作數中，因此8086指令集的如此設計大大提高了代碼密度（code density）。

相比與8080或8085，8086有更強的寄存器通用性，但比起典型的小型計算機來說仍有很大不如。8086的某些指令隱式使用某些寄存器。比起更為規則的16- 或32-位處理器如PDP-11, VAX, 68000,等等，8086的寄存器分配特性使得編譯器的工作更為複雜;另一方面，比起半現代化的但廣為使用的8位微處理器如6502, 6809,或8085,編譯器生成代碼就容易太多了.

8086有64 KiB的字寬為8位的（或32K個寬度為16位的）I/O尋址空間.

棧的長度最大為64 KB（一個段），8086硬體支持棧頂由高地址向低地址生長。入棧、彈棧的數據單元長度為2位元組。棧頂位置由寄存器的組合SS:SP給出.

共有256 個中斷,包括硬體中斷與軟中斷。中斷可以嵌套，使用棧來保存中斷返回地址。

8086增加了一些8080與8085所沒有的新指令，用於更好地支持Pascal與PL/M的高級程序設計特性;如push mem-op, ret size, (其它一些指令如push immed and enter,在隨後的80186, 80286, and 80386中陸續增加.)

### 標誌寄存器

8086有一個16位寬的標誌寄存器FLAGS.其中9個比特是被使用的，另外7個比特保留未用。具體是：進位標誌CF、奇偶標誌PF、輔助標誌AF、為零標誌ZF、符號標誌SF、追蹤標誌TF、中斷允許標誌IF、方向標誌DF、溢出標誌OF。

### 記憶體分段

在Intel的8位、16位處理器中，由於寄存器的寬度為8或16比特，而地址總線的寬度一般是要大於寄存器的寬度，所以為了能訪問整個地址空間，需要採取特殊的尋址計算——分段尋址。從80386開始的32位微處理器，地址總線寬度也是32比特，可以視作扁平（flat）地址空間，不再需要分段尋址。

8086的分段尋址，是指一個物理地址由段地址（segment selector）與偏移量（offset）兩部分組成，長度各是16比特。其中段地址左移4位（即乘以16）與偏移量相加即為物理地址。例如，06EFh:1234h，表示段地址為06EFh,偏移量為1234h,物理地址為06EF0h + 1234h = 08124h。在計算物理地址時如果發生上溢出，8086處理器捨棄進位。例如，FFFFh：0010h所對應的物理地址為00000h.

一個20位的物理地址對應著4096個不同的"段地址：偏移量"的組合。這是因為，偏移量的最低4位對應於物理地址的最低四位，而偏移量的高12位共有4096個取值。

段地址所對應的物理地址的粒度是16位元組（=24），稱之為paragraph。

段地址確定後，偏移量的取值最多為64KiB（=65536），這就是分段尋址的最大的線性地址空間。實際上在8086處理器上，作業系統分配的線性地址空間可以是不大於64KiB的任意數量。程序可以自由訪問整個物理記憶體空間，作業系統沒有任何權限限制或監管（supervision）。

8086處理器有20根地址總線引腳（pins），因此物理記憶體空間最大為220=1MiB=1,048,576位元組。這還包括了IO記憶體。因此使用8086處理器的電腦的主存的容量少於1MiB，最常見的主存容量是640KiB。

由於X86指令集的向後兼容原則，計算物理地址時，段地址左移4位被所有後繼支持實模式的X86處理器所繼承。80286是使用24位寬地址總線的16位微處理器，如果要在整個224=16MiB物理存儲空間尋址，就必須採用其它方式，即保護模式尋址，這時16位的段地址是指向段描述符表（segment descriptors table）的一個包含24位基地址的條目，基地址加上16位偏移量即為24位的物理地址。

在8086上運行的編譯器，一般支持兩種C語言的指針：近指針（near）與遠指針（far）。近指針是16位的地址偏移值，隱式與程序的代碼段地址或數據段地址結合使用以確定物理地址。遠指針是32位的「段地址：偏置量」成對出現，用以確定20位的物理地址。某些編譯器支持「巨指針」（huge），類似於遠指針。但巨指針的地址運算是線性20位；而遠指針的地址運算在16位偏移值溢出時不影響段地址部分，因此遠指針的線性部分是16位。

為了避免對大量的指針、數據結構、函數指出是「近」還是「遠」，編譯器提供了記憶體模式（memory model）給出了預設的記憶體訪問方式：

- 微模式tiny數據段代碼段共用不超過64K記憶體空間，編譯為.com可執行文件。
- 小模式small數據段、代碼段各用最多64K記憶體共空間。
- 緊湊模式compact數據段> 64K。
- 中模式medium代碼段> 64K。
- 大模式large代碼段數據段都> 64K。
- 巨模式huge單個陣列> 64K。

預編譯庫對不同的記憶體模式要分別編譯為相應版本。

#### 移植老的程序

8位機上的程序可以不考慮段地址直接以.com可執行文件以「微模式」在8086上運行。這是當時8086與MS-DOS作為新平台取得市場成功的關鍵原因——大量已存的CP/M應用程式能很快得到利用。

### 實例代碼

以下8086/8088彙編原始碼是將給定大小的數據塊從一個位置拷貝到另一個位置的叫做`_memcpy`的子程序。每次拷貝一個字節，數據移動和循環邏輯採用16位操作。

```











0000:1000

0000:1000
0000:1000 55
0000:1001 89 E5
0000:1003 06
0000:1004 8B 4E 06
0000:1007 E3 11
0000:1009 8B 76 04
0000:100C 8B 7E 02
0000:100F 1E
0000:1010 07

0000:1011 8A 04
0000:1013 88 05
0000:1015 46
0000:1016 47
0000:1017 49
0000:1018 75 F7

0000:101A 07
0000:101B 5D
0000:101C 29 C0
0000:101E C3
0000:101F
```

```nasm
; _memcpy(dst, src, len)
; Copy a block of memory from one location to another.
;
; Entry stack parameters
;      [BP+6] = len, Number of bytes to copy
;      [BP+4] = src, Address of source data block
;      [BP+2] = dst, Address of target data block
;
; Return registers
;      AX = Zero

            org     1000h       ; Start at 0000:1000h

_memcpy     proc
            push    bp          ; Set up the call frame
            mov     bp,sp
            push    es          ; Save ES
            mov     cx,[bp+6]   ; Set CX = len
            jcxz    done        ; If len=0, return
            mov     si,[bp+4]   ; Set SI = src
            mov     di,[bp+2]   ; Set DI = dst
            push    ds          ; Set ES = DS
            pop     es

loop        mov     al,[si]     ; Load AL from [src]
            mov     [di],al     ; Store AL to [dst]
            inc     si          ; Increment src
            inc     di          ; Increment dst
            dec     cx          ; Decrement len
            jnz     loop        ; Repeat the loop

done        pop     es          ; Restore ES
            pop     bp          ; Restore previous call frame
            sub     ax,ax       ; Set AX = 0
            ret                 ; Return
            end proc
```

上面的代碼使用BP（基指針）寄存器建立一個調用幀（包含子程序執行過程中的所有參數和局部變量的棧的區域）。這種調用約定支持可重入和遞歸代碼，大多數類ALGOL語言上世紀50年代後期就已採用這種約定。ES段寄存器保存在堆棧上，並用DS段寄存器中的值替換，於是指令MOV AL會在相同的源數據段和目的數據段之間操作。在返回前，子程序恢復了ES寄存器以前的值。

上面的子程序是一種比較麻煩的拷貝成塊數據的方法。若源數據區域和目的數據區域都是處於單個65,536位元組的段中（上述程序的要求），使用8086的塊`MOV`指令更有優勢。上面的循環部分可以替換為：

```
0000:1011 F2
0000:1012 A5
```

```nasm
loop        rep                  ; Repeat until CX=0
            movsw                ; Move the data block
```

這種方法每次可以拷貝一個字。`REP`指令使下面的`MOVSW`重複直至CX=0，重複之時自動遞增SI和DI。另外，`MOVSB`或`MOVSD`指令可以用來一次拷貝單個字節或雙字。大多數彙編器在`REP`指令用在`MOVSW`之前為`REP MOVSW`的情況下都會正確彙編。

如果被中斷，這個程序也能正確運行，因為程序計數器會繼續指向`REP`指令，知道塊拷貝已經結束。拷貝會在中斷服務程序將控制恢復的時候繼續。

### 性能

外部總線作為數據與地址的復用，降低了CPU的性能。取16比特或8比特數據需要4個時鐘周期。指令的長度為1-6個字節，取指令與執行是並發完成的。CPU內的總線界面單元把取到的指令保存在6位元組的預取隊列中。

指令｜register-register｜register immediate｜register-memory｜memory-register｜memory-immediate

mov｜2｜4｜8+EA｜9+EA｜10+EA

ALU｜3｜4｜9+EA,｜16+EA,｜17+EA

jump｜register => 11 ; label => 15 ; condition,label => 16

整數乘法｜70~160（取決於操作數data以及大小）加上EA

有符號整數除法｜80~190（取決於操作數data以及大小）加上EA

- EA = 計算有效地址的時間，5到12周期.
- 用時為最好的情形,依賴於預取狀態,指令對齊,及其它因素.

8086涉及記憶體訪問的指令，包括跳轉（jump）指令需要比8080與8085更多的時間，原因在於：

- 取指令與執行指令是鬆散耦合，對於跳轉與隨機數據訪問沒有特殊措施，效率較低.
- 沒有專門的地址計算部件，只能用主ALU，雖然有專用的段地址 + 偏移地址的加法器
- 外部地址總線與數據總線是多工復用，與8位處理器相比要多用33~50%的總線周期

8086的後繼處理器的記憶體訪問性能大大增強了。80186與80286有專門的地址計算硬體，節約了很多時間周期。80286的外部地址總線與數據總線是分開各自專用的。

### 浮點

8086/8088可以連接上專用的數學協處理器以增加浮點計算性能。標準的數學協處理是Intel 8087，執行80位浮點數運算。

## 微電腦

第一個以8086為基礎的商業微電腦是Mycron2000。

IBM Displaywriter文字處理機也使用8086。在大部分顯要的所有微處理器中，IBM PC使用了更窄的記憶體總線版本的8086，也就是Intel 8088。

8086CPU結構上的一個重要特點是分為二大部分，即總線接口單元BIU和執行單元EU，其中BIU負責外部資訊交換，EU負責指令執行，二者合理分工、並行工作，工作效率比此前的CPU明顯提高，常稱之為流水線結構。

## 硬體模式

8086和8088支持兩種硬體模式：最大模式和最小模式。最大模式是用在大型應用中的多重處理，並且也是支持8087處理器的必需。該模式通常硬體化在電路中，不能由軟體改變。具體來說，#33引腳（MN/MX）是連載高電壓還是接地決定了模式。改變引腳#33的狀態就改變了其他特定引腳的功能，這些引腳中大多數是CPU處理（局部）總線的。IBM PC和PC/XT使用工作在最大模式的Intel 8088，讓CPU與PC或PC/XT主板上安裝在數學協同處理器插口的可選8087協同處理器。（PC和PC/XT可能因為其他原因而要求用最大模式，比如要支持DMA控制器。）

## 外設

- Intel 8237：直接存儲器存取（DMA）控制器
- Intel 8251：USART
- Intel 8253：可編程間隔定時器
- Intel 8255：可編程序外圍接口
- Intel 8259：可編程中斷控制器
- Intel 8279：鍵盤/顯示控制器
- Intel 8282/8283：8位鎖存器
- Intel 8284：時鐘發生器
- Intel 8286/8287：雙向8位驅動
- Intel 8288：總線控制器
- Intel 8289：總線仲裁器

## 紀念

2018年6月8日，英特爾在官網上開啟了限量抽獎活動，獎品為i7-8086k，限量生產8086個。

## 外部連結

- Intel 8086/8088 images and descriptions at cpu-collection.de
- 8086 visual microprocessor emulator

Article based on Intel 8086 at FOLDOC，used with permission.

## 基本資料

- 產品化：1978年
- 產品化：1990年代
- CPU主頻範圍：5
- CPU主頻範圍：MHz
- CPU主頻範圍：10
- manuf1：Intel、AMD、NEC、富士通、哈里斯公司（Intersil）、OKI、西門子公司、德州儀器、三菱集團、松下電器
- 指令集架構：x86-16
- pack1：40針腳DIP
- 前任：Intel 8080
- 相關產品：8088
- 繼任：80186和80286（兩種晶片均在1982年引進）
- 製作工藝/製程：3 μm
- transistors-from：29,000