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字节(包括前缀)。