PCjs Machines

Home of the original IBM PC emulator for browsers.


PCjs Blog

Early 80386 CPUs

Assembling a detailed and accurate history of the Intel 80386 CPU, including a complete listing of all the “steppings” (revisions), when they were released, what “errata” (problems) each stepping suffered from, and which of those problems were fixed by a later stepping, seems virtually impossible at this late date.

See Intel 80386 CPU Information for what the PCjs Project has been able to collect about 80386 steppings so far, including how some of them were externally marked and internally identified, along with lists of associated errata, much of it based on Intel’s own documents.

Of course, there’s also an eclectic mix of information about early 80386 processors available from various online sources. A few of those are highlighted below.

Excerpt from “Inside Track”, PC Magazine, February 24, 1987, by John C. Dvorak:

80386 Bug Stopper Dept.: If you buy an 80386 machine, card, or chip, make sure you get the B1 revision of the chip or something newer (B2, B3, and so on). There are far too many bugs in the A1 and A2 versions of the chip to be acceptable. Here’s what to look for: On the top line of the chip you’ll see the designation A80386-16. If it says A80386-16ES, then it’s an engineering sample and the vendor is a cheapskate. The samples have the revision number on the top line as A1, A2, or B1. Look no further.

For the rest of you, look at the second line on the chip. If it’s S40344 then you have a B1 chip. S40334 is the A2 revision and S40276 is the A1 revision….

Excerpt from “Tutor”, PC Magazine, October 15, 1991, by Jeff Prosise:

You can tell if you have a B0 or B1 Step level 386 by looking at the markings on the chip. If it has the ID number S40336 or S40337 stamped on it, then it’s a Step B0; if it’s marked with S40343, S40344, or S40362, it’s a Step B1. Some B0 and B1 chips were marked B0 or B1 rather than with an ID number.

Excerpt from “CPU Identification by the Windows Kernel”, by Geoff Chappell:

Finer identification of 80386 processors is largely academic. Whatever the model or stepping, the 80386 processor is unsupported since [Windows NT] version 4.0, and soon causes the bug check UNSUPPORTED_PROCESSOR (0x5D), though not without the kernel having worked its way through more tests for defects to identify models and steppings. For any 80386 processor that passes all tests, the model and stepping leap ahead to 3 and 1. Version 3.51, which was the last to support the 80386 (and only then in a single-processor configuration), rejects any 80386 that does not pass all these tests.

Family  Model   Stepping    Test
------  -----   --------    ----
  3       0       0         32-bit MUL not reliably correct
  3       1       0         supports XBTS instruction
  3       1       1         set TF bit (0x0100) in EFLAGS causes Debug exception (interrupt 0x01) only at completion of REP MOVSB
  3       3       1

The particular multiplication that distinguishes model 0 is of 0x81 by 0x0417A000. This same test was used by Microsoft at least as far back as Windows 3.10 Enhanced Mode, to advise:

The Intel 80386 processor in this computer does not reliably execute 32-bit
multiply operations. Windows usually works correctly on computers with this
problem but may occasionally fail. You may want to replace your 80386 processor.
Press any key to continue...

The instruction whose support is tested for model 1 stepping 0 has opcode bytes 0x0F 0xA6 followed by a Mod R/M byte and by whatever more this byte indicates is needed for the operand. This opcode is disassembled as XBTS by Microsoft’s DUMPBIN utility from Visual C++, and has been since at least the mid-90s. However, the same opcode was apparently reused for the CMPXCHG instruction on some 80486 processors. The confusion seems to have left a lasting mark: Intel’s opcode charts leave 0x0F 0xA6 unassigned even now. The specific test performed by the Windows kernel is to load EAX and EDX with zero and ECX with 0xFF00. If executing XBTS ECX,EDX does not cause an Invalid Opcode exception and clears ecx to zero (which CMPXCHG ECX,EDX would not), then XBTS is deemed to be supported and the processor is model 1 stepping 0. This case of 80386 processor also was known to Windows 3.10 Enhanced Mode, and was rejected as fatal:

Windows may not run correctly with the 80386 processor in this computer.

Upgrade your 80386 processor or start Windows in standard mode by typing
WIN /s at the MS-DOS prompt.

When string instructions such as MOVSB are repeated because of a REP prefix, each operation is ordinarily interruptible. As Intel says (for REP in the Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 2B: Instruction Set Reference N-Z), this “allows long string operations to proceed without affecting the interrupt response time of the system.” It ordinarily applies also to the Debug exception, such as raised by the processor at the end of executing an instruction for which the TF bit is set in the EFLAGS when the instruction started. Programmers may have noticed this in the real world of assembly-language debugging. If the debugger actually does implement its trace command as a trace, as opposed to setting an INT 3 breakpoint where the instruction is calculated to end, then a two-byte REP MOVSB may take many keystrokes to trace through! That model 1 stepping 1 traces through a REP MOVSB without interruption may be helpful when debugging, but it is surely a defect.

More examples of problems with early 80386 CPUs are posted in “The Old New Thing” blog. Here are some highlights from “My, what strange NOPs you have!”, by Raymond Chen:

[I]f the instruction following a string operation (such as movs) uses opposite-sized addresses from that in the string instruction (for example, if you performed a movs es:[edi], ds:[esi] followed by a mov ax, [bx]) or if the following instruction accessed an opposite-sized stack (for example, if you performed a movs es:[edi], ds:[esi] on a 16-bit stack, and the next instruction was a push), then the movs instruction would not operate correctly….

[T]here was one bug that manifested itself in incorrect instruction decoding if a conditional branch instruction had just the right sequence of taken/not-taken history, and the branch instruction was followed immediately by a selector load, and one of the first two instructions at the destination of the branch was itself a jump, call, or return. The easy workaround: Insert a NOP between the branch and the selector load….

[T]he B1 stepping did not support virtual memory in the first 64KB of memory. Fine, don’t use virtual memory there….

If virtual memory was enabled, if a certain race condition was encountered inside the hardware prefetch, and if you executed a floating point coprocessor instruction that accessed memory at an address in the range 0x800000F8 through 0x800000FF, then the CPU would end up reading from addresses 0x000000F8 through 0x0000000FF instead. This one was easy to work around: Never allocate valid memory at 0x80000xxx.

Jeff Parsons
Feb 23, 2015