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设计

<image>

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的指令集仍然是PC机与服务器的基本指令集。

## 细节

<image>

<image>

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`指令，知道块拷贝已经结束。拷贝会在中断服务程序将控制恢复的时候继续。

### 性能

<image>

外部总线作为数据与地址的复用，降低了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