简介:这份PDF文档是Intel 8086微处理器的官方技术手册(1990年9月版,Order Number 231455-005),面向学习微机原理、汇编语言与计算机体系结构的学生、嵌入式开发者及硬件工程师,用于系统理解8086的架构、引脚定义与工作模式。手册完整覆盖8086/8086-2/8086-1三种型号,包括5/8/10MHz时钟频率、HMOS-III工艺、40引脚CERDIP与塑料封装、1MB直接寻址、14个16位寄存器、24种操作数寻址模式,以及位、字节、字和块操作与二进制/十进制乘除运算;同时详述引脚功能、最小与最大模式下的局部总线接口、中断应答三态行为及MULTIBUS兼容性。资源包仅含1个PDF文件,大小约629KB,轻量便携,适合随时查阅。已有632人学习下载,可作为课程学习、硬件设计与汇编开发的案头参考。
1. 8086 手册:为什么一条 16 位总线能同时装下地址和数据
翻 8086 手册的引脚表,最先让人愣住的不是那 14 个寄存器,而是 AD0~AD15 这组名字——地址和数据挤在同一组线上。40 个引脚要干完 16 位数据、20 位地址、控制信号、中断、总线仲裁所有的事,引脚数根本不够分。8086 的解法是时间复用:T1 周期这 16 根线输出地址,T2 到 T4 周期同一组线传数据。这个设计不是省成本的小聪明,它直接决定了整块主板必须配地址锁存器,也决定了后来所有 x86 总线时序的基本骨架。搞懂这套复用机制,你再看 8086 的 24 种寻址模式、6 字节指令队列、最小/最大模式切换,就不再是零散的知识点,而是同一套引脚预算约束下推出来的必然结果。这份 1990 年的手册把每个引脚的每个时钟周期都写死了,正好是拆解这套机制最完整的底稿。
2. 引脚复用:20 位地址怎么塞进 16 根线
2.1 AD0~AD15 的分时协议与 ALE 锁存
8086 的地址总线是 20 位,数据总线是 16 位,如果各走各的,光这两组就要 36 根引脚,加上控制、电源、地,40 脚封装根本装不下。手册给出的方案是把低 16 位地址和 16 位数据合并到 AD0~AD15,高 4 位地址和状态位合并到 A19/S3~A16/S6。关键在于每个总线周期被切成 T1、T2、T3、TW、T4 五个状态,T1 期间 AD 线上是地址,T2 开始切换成数据。
这个切换必须有人告诉外部电路“现在线上是地址,赶紧锁走”。手册里 ALE(Address Latch Enable,引脚 25)就是干这个的:它在 T1 期间输出一个高脉冲,外部 8282/8283 锁存器在这个脉冲的下降沿把 AD0~AD15 和 A19~A16 上的地址锁进寄存器,之后 AD 线怎么变都无所谓了。手册特意注明 ALE 永远不会被浮空,因为它是整个地址通路的节拍器,一旦丢了,CPU 就不知道当前周期访问的是哪个地址。
我一般会这么理解这套时序:T1 是“报地址”,ALE 是“拍照存证”,T2 之后是“搬数据”。没有 ALE,地址和数据在时间上重叠,外部存储器根本分不清 T1 的 AD 线值和 T2 的 AD 线值哪个是地址哪个是数据。这也是为什么 8086 最小系统里 8282 锁存器不是可选项而是必需品——它不是缓冲,是解复用器。
2.2 BHE 与 A0:一个字节到底落在高半还是低半
16 位数据总线带来一个麻烦:内存是按字节编址的,一个 16 位字可以放在偶地址(对齐),也可以放在奇地址(不对齐)。手册用 BHE(Bus High Enable,引脚 34)和 A0 的组合来编码四种传输情况:
| BHE | A0 | 传输内容 |
|---|---|---|
| 0 | 0 | 整字(16 位) |
| 0 | 1 | 高字节,来自奇地址 |
| 1 | 0 | 低字节,来自偶地址 |
| 1 | 1 | 不传输 |
BHE 低有效,控制 D15~D8 这半边的使能;A0 则对应 D7~D0 这半边。手册里有一句容易被略过的话:A0 在功能上类似于 BHE,只是管低字节。这意味着 8 位外设接在总线低半时,用 A0 做片选条件;接在高半时用 BHE。两个信号合起来,一个 16 位总线就能同时服务字节设备和字设备,不需要额外的数据交换逻辑。
这里有个血泪经验:如果外部存储器只接了低 8 位,BHE 悬空不处理,读高字节时会读到浮空值,程序里表现为某个结构体字段莫名其妙变成 0xFF。手册把 BHE 标成 active LOW 且 T1 期间有效,就是在提醒你它必须被显式驱动,不能当摆设。
2.3 状态复用:A19/S6~A16/S3 在 T2 之后变成了什么
高 4 位地址线在 T2 之后不再需要传地址(20 位地址在 T1 已经锁存),于是这 4 根线被复用成状态信号 S6~S3。手册给出了 S4、S3 的编码表:
S4 S3 当前使用的段寄存器 0 0 ES(附加段) 0 1 SS(堆栈段) 1 0 CS(代码段)或 none 1 1 DS(数据段)S5 是中断允许标志位的影子,每个 CLK 周期开头更新;S6 恒为 0。这套状态编码的价值在于外部逻辑可以实时知道 CPU 当前在访问哪个段,配合 8288 总线控制器做存储器保护或者调试追踪。对写仿真器的人来说,S4/S3 是判断“这条指令到底读了哪个段”的唯一硬件线索,比反汇编猜要可靠得多。
3. 总线周期与 READY:CPU 怎么知道存储器跟不跟得上
3.1 T1~T4 的状态机与等待周期插入
8086 的一个基本总线周期是 4 个时钟:T1 发地址、T2 发控制信号(RD/WR 等)、T3 采样数据、T4 结束。但真实存储器不一定 4 个周期就能响应,手册用 READY(引脚 22)解决这个问题:READY 为高时,CPU 在 T3 之后插入一个 TW(等待)周期,每个 TW 都重新采样 READY,直到它为高才进入 T4。
手册里有一句很关键的警告:8086 的 READY 输入是不同步的,如果建立/保持时间不满足,正确操作无法保证。所以标准做法是让 8284A 时钟发生器对 READY 做同步,产生干净的 READY 给 CPU。我见过有人直接把慢速存储器的片选信号接到 READY,结果系统跑起来时好时坏——因为片选信号的边沿和 CLK 没有固定相位关系,偶尔就踩在采样窗口上翻车。
READY 机制的本质是把总线周期的长度从固定 4 拍变成可变长度,代价是 CPU 在 TW 期间什么也不干。手册没有明说但可以从时序推出来:TW 期间地址和控制信号保持有效,数据线处于采样等待,所以外设只要在任意一个 TW 结束前把数据放稳就行,不需要精确对齐 T3。
3.2 最小模式与最大模式:同一组引脚的两套语义
8086 有一个 MN/MX 引脚(引脚 33),接高电平是最小模式,接低电平是最大模式。这个引脚改变的不是 CPU 内部逻辑,而是引脚 24~31 这 8 根线的含义。最小模式下它们是 M/IO、WR、INTA、ALE、DT/R、DEN、HOLD、HLDA,直接给外部电路用;最大模式下变成 S2/S1/S0、RQ/GT0、RQ/GT1、LOCK、QS1/QS0,配合 8288 总线控制器生成实际的控制信号。
手册把两种模式的引脚描述分开列,但共用同一张 40 脚图。这种设计的动机很直接:小系统不需要总线控制器,CPU 自己发控制信号就够了;多处理器系统需要仲裁和状态编码,就把控制信号生成外包给 8288,CPU 只输出状态码。S2/S1/S0 的编码表覆盖了中断应答、I/O 读写、取指、内存读写、暂停、被动七种总线操作,8288 解码后产生对应的 MEMR、MEMW、IOR、IOW 等信号。
从机制上看,最大模式是把“控制信号生成”从 CPU 里挪到外部,换来的是 RQ/GT 总线仲裁和 LOCK 指令前缀的支持。LOCK 引脚在最大模式下有效,配合 LOCK 前缀指令可以锁住总线,保证一条读-改-写指令(比如 XCHG 或者带 LOCK 的 ADD)不被其他总线主设备打断。最小模式没有这个能力,因为引脚不够。
4. 指令队列:6 字节 FIFO 怎么把取指和执指拆开
4.1 BIU 与 EU 的异步分工
手册的功能描述部分把 8086 内部划成两个单元:总线接口单元 BIU 和执行单元 EU。BIU 负责取指、取操作数、地址重定位和总线控制;EU 负责解码和执行指令。两者大部分时间异步工作,BIU 只要发现指令队列里有至少 2 字节空位,就会发起一次字取指,把指令流填进队列。
这个队列是 6 字节的 FIFO。EU 从队列头部取指令字节,BIU 从内存往队列尾部填。手册说得很清楚:队列的存在让 BIU 能持续占用内存总线,减少“死时间”。在 8086 之前,CPU 取一条指令、执行、再取下一条,取指期间执行单元闲着;有了队列,取指和执行重叠,等效于提高了总线带宽利用率。
我一般会这么理解:这不是现代意义上的流水线,EU 并没有把一条指令拆成多级,它只是把“取指”这个动作提前做了。真正的收益出现在顺序执行的代码里——BIU 提前把后面几条指令取进队列,EU 执行完当前指令立刻能从队列拿到下一条,不用等内存。遇到跳转指令时队列被清空,EU 要等 BIU 重新取指,这就是手册里说的“队列为空时第一个字节立即对 EU 可用”的场景。
4.2 QS1/QS0:外部怎么看见队列在干什么
最大模式下引脚 24、25 是 QS1 和 QS0,手册给出了队列状态编码:
QS1 QS0 含义 0 0 无操作 0 1 从队列取出操作码的第一个字节 1 0 队列已空 1 1 从队列取出后续字节这组信号在队列操作执行后的那个 CLK 周期有效。它的用途是让外部逻辑(比如指令跟踪器或者协处理器接口)实时知道 CPU 的取指进度。8087 协处理器就是靠 QS 信号和 8086 保持同步的:当 8086 从队列取出 ESC 指令(协处理器指令),8087 通过 QS 看到这个动作,开始接管后续的总线周期。
从设计取舍看,QS 信号暴露了 CPU 内部队列的状态,这在当时是很不寻常的——大多数 CPU 不对外暴露微架构细节。8086 这么做是为了支持多处理器配置,让协处理器能跟上主 CPU 的指令流。代价是队列状态变成了架构约定的一部分,后续 x86 处理器如果要保持兼容,队列行为就不能随便改。
4.3 队列清空与跳转惩罚
手册没有给出具体的跳转惩罚周期数,但从机制上可以推出来:任何改变 CS:IP 的指令(JMP、CALL、RET、条件跳转成立)都会导致 BIU 丢弃队列里已预取的字节,从新地址重新取指。队列越深,丢弃的字节越多,但 6 字节的深度在当时是权衡过的——太深则跳转惩罚大,太浅则预取收益小。
这里有个容易踩的坑:自修改代码。如果程序在运行时改写了自己后面的指令,BIU 可能已经把旧指令预取进队列了,EU 执行的是队列里的旧字节而不是内存里的新字节。手册在指令集部分提到需要跳转或者序列化操作来刷新队列,但很多早期程序员没注意到这一点,调试时看到“代码改了但没生效”,查半天以为是存储器写失败。实际上写是成功的,只是队列里还留着旧指令。
5. 避坑:引脚时序与模式配置的常见翻车
5.1 ALE 脉冲被拉长导致地址锁存错位
现象:系统偶尔访问到错误地址,复位后概率性出现。
原因:ALE 是 T1 期间的高脉冲,宽度由 CLK 决定。如果外部电路对 ALE 做了 RC 滤波或者用慢速逻辑门整形,脉冲边沿变缓,8282 锁存器可能在 AD 线已经切换到数据之后才采样,锁进去的是数据而不是地址。
解决:ALE 直接驱动锁存器的 CLK 输入,不经过任何整形;如果必须缓冲,用高速 CMOS 缓冲器,传播延迟控制在几个 ns 以内。手册标注 ALE 从不浮空,就是让你放心把它当纯时序信号用。
5.2 READY 未同步导致偶发读写失败
现象:接慢速 EPROM 时,大部分时候正常,高温或者换批次芯片后随机出错。
原因:8086 的 READY 输入内部不同步,要求满足建立/保持时间。直接把存储器片选或者地址译码信号接到 READY,边沿和 CLK 没有固定关系,偶尔违反时序。
解决:用 8284A 的 READY 同步输出,或者自己用两级触发器对 READY 做 CLK 同步。手册明确写了“正确操作不保证”,这不是建议,是硬约束。
5.3 MN/MX 接错导致控制信号全乱
现象:最小模式电路按手册连好,上电后 WR、RD 不动,系统不启动。
原因:MN/MX 引脚接反。接高是最小模式,接低是最大模式。最大模式下引脚 24~31 变成 S2/S1/S0、RQ/GT、LOCK、QS,不再输出 WR、RD、ALE 等信号,外部电路等不到控制信号。
解决:先量 MN/MX 电平,确认和电路设计一致;最大模式必须配 8288,不能直接把 S2/S1/S0 当读写信号用。
5.4 BHE 悬空导致高字节读到浮空值
现象:16 位读操作低字节正确,高字节恒为 0xFF 或随机值。
原因:BHE 控制 D15~D8 的使能,悬空时高半边缓冲器状态不确定。手册标注 BHE 低有效,T1 期间有效,必须被驱动。
解决:BHE 接上拉电阻保证默认高(高字节不传输),需要传高字节时由地址译码逻辑拉低;或者直接用 A0 和 BHE 一起做字节选择逻辑。
5.5 中断应答期间 AD 线浮空被误读
现象:中断控制器接在 AD 线上,中断应答时读到全 1 或者随机向量号。
原因:手册写明 AD0~AD15 在中断应答和 hold acknowledge 期间浮空到三态 OFF。如果外部中断控制器指望从 AD 线读中断向量,必须自己驱动或者从其他路径获取。
解决:中断向量通过 INTA 周期从外部中断控制器送到数据总线,但 8086 在第一个 INTA 周期浮空 AD 线,第二个 INTA 周期才读向量。外部逻辑要在第二个 INTA 周期驱动数据线,不能依赖 CPU 输出。
6. 从 20 位地址到 1MB 空间:段机制的取舍与验证
8086 用两个 16 位寄存器拼出 20 位物理地址:段寄存器左移 4 位加上偏移量。这个设计让 16 位寄存器能寻址 1MB 空间,代价是同一个物理地址有多种段:偏移表示,而且段边界每 16 字节就重叠一次。手册在存储器组织部分明确写了段从 16 字节边界开始,代码、数据、附加数据、堆栈四种段各最多 64KB。
这个设计的取舍很值得琢磨。如果直接用 20 位地址寄存器,指令编码会变长,寄存器数量也要增加;用段寄存器加偏移,指令里只需要 16 位偏移,段寄存器由硬件根据操作类型自动选择(取指用 CS、堆栈操作用 SS、数据访问用 DS 或 ES)。手册说这样“程序更短、更快、更有结构”,但代价是程序员要时刻注意段寄存器的值,而且指针比较不能直接比偏移量。
验证段地址计算是否正确,最直接的办法是用手册里的公式手算几个例子:物理地址 = 段值 × 16 + 偏移。比如 CS=0x1000、IP=0x0200,物理地址是 0x10200。如果开了 A20 线(8086 没有,但后续处理器有),还要考虑回绕。8086 本身没有 A20 门控,20 位地址线直接输出,段:偏移超过 0xFFFFF 时自然回绕到 0x00000,这是硬件行为不是软件约定。
从那以后我每次看早期 PC 的内存布局图,都会先确认段寄存器的初始值和 A20 状态,再谈“为什么这块内存访问不到”。手册把每个引脚的时序和每个寄存器的行为都写死了,但段机制的坑不在手册里,在手册没画出来的那张地址计算图上。希望这份底稿能帮你在翻 8086 手册时,不只看到引脚表,还看到引脚数约束下推出来的整套总线时序和寻址架构。
本文还有配套的精品资源,点击获取