## 14.7 与8086的不同
通常,80386会正确的执行ROM中为8086,8088,80186和80188设计的软件。下面是一些8086和80386在执行过程中的一些细微区别。
1.指令时钟计数。
大部分指令的执行过程,80386花费的时钟要比8086/8088要少。
这主要影响到一下几个方面:
+ I/O操作要求的延时。
+ 操作并口连接的80387过程中设定的延时。
2.DIV指令的除法异常触发点。
80386的除法异常总是将CS:IP指向失败的指令。8086/8088指向下一条指令。
3.未定义的8086/8088操作码。
执行在8086/8088中未定义的操作码将导致异常6或者执行80386中定义的新指令。
4.PUSH SP写入值。
80386在执行PUSH SP时放入堆栈的值与8086/8088不同。80386把增加SP作为指令的一部分,在SP改变之前将其入栈;8086/8088在增加SP之后再将其入栈。如果入栈的值很重要,用下面的三条指令代替PUSH SP:
```
PUSH BP
MOV BP, SP
XCHG BP, [BP]
```
上面的代码在80386中的执行结果和8086/8088中的PUSH SP。
5.超过31位的移位或循环移位。
80386将所有移位和循环移位操作计数的低位5位作为掩码。这种用32取模的操作将计数值限制为最多31位,以此来限制在执行中的代码被中断的时间。
6.冗余前缀。
80386限制指令的最大长度为15个字节。突破这个限制的唯一方法是在指令的前面使用冗余前缀。超过长度限制的指令将导致异常13。8086/8088没有指令长度限制。
7.操作数越过0或65536。
对于8086,试图访问的内存操作数的偏移值如果越过了65536(比如,MOV使用偏移值65536)或者0(比如,当SP=1时使用PUSH)将导致偏移值回绕到对65536取模后的值。80386在这种情况下将产生异常-13,如果使用了数据寄存器(比如,CS, DS, ES, FS或GS),-12,如果使用了堆栈寄存器(比如,SS)。
8.顺序执行越过了偏移值65536。
对于8086,顺序执行的指令越过了偏移值65536,处理器将取同一个段内的0地址处的下一条指令。这种情况下,80386将产生异常13。
9.某些指令禁止使用LOCK。
LOCK前缀以及相应的输出信号应该只是用来阻止总线控制器在数据移动过程中被中断。80386在使用XCHG指令和存储器交互时总是声明LOCK信号(即使没有使用LOCK前缀)。LOCK应该只是用在更新存储器的下列指令之前:BTS, BTR, BTC, XCHG, ADD, ADC, SUB, SBB, INC, DEC, AND, OR, XOR, NOT和NEG。在其他任何指令之前使用LOCK将导致未定义操作码异常(中断6)。
10.单步执行外部中断处理函数。
80386单步异常的优先级不同于8086/8088。这个改变使得在程序在单步执行时阻止外部中断进入后被单步执行。80386单步异常的优先级高于任何外部中断。由INT指令或异常产生的中断进入处理函数后,80386仍将单步执行。
11.IDIV指令80H或8000H商异常。
80386的IDIV指令的商可以是最大的负数。8086/8088产生异常0。
12.堆栈中的标志位。
由PUSHF,中断,异常存储的标志位在位12-15上与8086不同。在8086上,它们被作为一个整体,而在80386上,位15总是0,位14-12反映的是最后装入它们的值。
13.NMI中断和NMI处理函数。
在80386识别出NMI中断后,NMI中断保持屏蔽,直到执行了IRET指令。
14.协处理器错误指向中断16。
任何带有协处理器的80386系统必须使用中断向量16来指向协处理器错误异常。如果8086/8088使用另一个向量,那么这两个向量都要指向协处理器错误异常处理函数。
15.数字异常处理函数应该允许前缀。
在80386上,为协处理器异常保存的CS:IP指向ESC指令之前的任何前缀。在8086/8088上,保存的CS:IP指向ESC指令。
16.协处理器不使用中断控制器。
协处理器错误信号在80386上不会进入中断控制器(8087 INT信号这样做)。如果在协处理器错误处理函数中有些指令是用来处理中断控制器的,就需要删除它们。
17.6个新的中断向量。
80386增加了6个异常,它们只有在8086程序有隐含错误的时候才发生。建议按照非法操作来增加这些异常的处理函数。这些增加的代码不会明显的影响现有的8086软件,因为这些中断正常情况下不会发生。这些中断标识符应该还没有被8086软件使用,因为它们被Intel放在了保留区域。表14-2描述了这6个新异常。
18.1M地址回绕。
实地址模式下,80386在1M地址外不会回绕。在8086家族中,可能声明的地址超过1M大小。例如,选择符是0FFFFH,偏移值是0FFFFH,有效地址将是10FFFFH(1M + 65519)。8086,最长20位地址,截断高位。然而80386可以有32位长地址,因此不会截断这样的地址。
Table 14-1. 80386 Real-Address Mode Exceptions
```
Description Interrupt Function that Can
Return Address
Number Generate the Exception
Points to Faulting
Instruction
Divide error 0 DIV, IDIV
YES
Debug exceptions 1 All
Some debug exceptions point to the faulting instruction, others to the
next instruction. The exception handler can determine which has occurred by
examining DR6.
Breakpoint 3 INT
NO
Overflow 4 INTO
NO
Bounds check 5 BOUND
YES
Invalid opcode 6 Any undefined opcode or LOCK
YES
used with wrong instruction
Coprocessor not available 7 ESC or WAIT
YES
Interrupt table limit too small 8 INT vector is not within IDTR
YES
limit
Reserved 9-12
Stack fault 12 Memory operand crosses offset
YES
0 or 0FFFFH
Pseudo-protection exception 13 Memory operand crosses offset
YES
0FFFFH or attempt to execute
past offset 0FFFFH or
instruction longer than 15
bytes
Reserved 14,15
Coprocessor error 16 ESC or WAIT
YES
Coprocessor errors are reported on the first ESC or WAIT instruction
after the ESC instruction that caused the error.
Two-byte SW interrupt 0-255 INT n
NO
```
Table 14-2. New 80386 Exceptions
| Interrupt | Function |
| --- | --- |
| Identifier | |
| 5 | BOUND指令以一个超出上限的寄存器值被执行。 |
| 6 | 未定义的操作码或LOCK应用与错误的指令。 |
| 7 | 执行ESC指令时,MSW中EM被置位。执行WAIT指令时TS被置位。 |
| 8 | 异常或中断向量超出了IDTR中定义的上限值;只有在用LIDT指令修改上限值才有可能发生这个错误,因为默认的3FFH对于256个中断ID足够了。 |
| 12 | 操作数越过了堆栈段边界,比如,用偏移值0FFFFH执行MOV或者SP=1时,执行压栈指令PUSH, CALL或INT。 |
| 13 | 操作数越过了段边界,不包括堆栈段;或者顺序指令执行试图跨越偏移值0FFFFH;或者指令长度超过了15字节(包括前缀)。 |
- 第一章 80386介绍
- 1.1 该手册的组织结构
- 1.2 其他文献
- 第二章 编程基本模型
- 2.1 存储器组织和段
- 2.2 数据类型
- 2.3 寄存器
- 2.4 指令格式
- 2.5 操作数选择
- 2.6 中断和异常
- 第4章 系统寄存器
- 4.1 系统寄存器 (System Registers)
- 4.2 系统指令 (System Instructions)
- 第五章 内存管理
- 5.1 分段地址转换(Segment Translation)
- 5.2 分页地址转换(Page Translation)
- 5.3 混合分段和分页地址转换(Combining Segment and Page Translation)
- 第六章 内存管理
- 6.1 为什么要保护(Why Protection?)
- 6.2 80386保护机制概述(Overview of 80386 Protection Mechnaisms)
- 6.3 段级保护(Segment-Level Protection)
- 6.4 页级保护(Page-Level Protection)
- 6.5 混合分页和分段保护(Combining Page and Segment Protection)
- 第7章 多任务(Multitasking)
- 8.1 I/O 寻址(I/O Addressing)
- 7.1 任务状态段(Task State Segment)
- 7.3 任务寄存器(Task Register)
- 7.4 任务门描述符(Task Gate Descriptor)
- 7.5 任务切换(Task Switching)
- 7.6 任务链(Task Linking)
- 7.7 任务寻址空间(Task Address Space)
- 第8章 输入 输出
- 8.2 I/O 指令(I/O Instructions)
- 8.3 保护和I/O(Protection and I/O)
- 第9章 异常和中断(Exceptions and Interrupts)
- 9.1 识别中断(Identifying Interrupts)
- 9.2 允许和禁止中断(Enabling and Disabling Interrupts)
- 9.3 同时发生的中断和异常的优先级(Priority Among Simultaneous Interrupts and Exceptions)
- 9.4 中断描述符表(Interrupt Descriptor Table)
- 9.5 IDT 描述符(IDT Descriptors)
- 9.6 中断任务和中断子程序(Interrupt Tasks and Interrupt Procedures)
- 9.7 出错码(Error Code)
- 9.8 异常条件(Exception Conditions)
- 9.9 异常总结(Exception Summary)
- 9.10 出错码总结(Error Code Summary)
- 第10章 初始化(Initialization)
- 10.1 复位后处理器状态(Processor State After Reset)
- 10.2 实模式初始化(Software Initialization for Real-Address Mode)
- 10.3 切换到保护模式(Switching to Protected Mode)
- 10.4 保护模式初始化(Software Initialization for Protected Mode)
- 10.5 初始化示例
- 10.6 TLB测试
- 第十四章 80386实地址模式
- 14.1 物理地址构成
- 14.2 寄存器和指令
- 14.3 中断和异常处理
- 14.4 进入和离开实地址模式
- 14.6 实地址模式异常
- 14.7 与8086的不同
- 14.8 与80286实地址模式的不同