Reverse-engineering the Intel 8086's instruction register
righto.com
righto.com
I am very grateful for your published work, thanks a lot !
The clock distribution itself doesn't seem to have any weird tricks, just the two clock lines that wind around the chip. I don't see any optimizations to equalize length to different parts of the chip, for instance.
> This is only a piece of the 8086's complex instruction handling. Other latches hold pieces of the instruction indicating register usage and the ALU operation, while a separate circuit controls the microcode engine...
I've always been amazed at how complex the 8086 instruction encoding is, especially compared to, say, the 6502 (which admittedly is a much less sophisticated microprocessor). I especially think this whenever I play around with a toy 8086 emulator I've written. :) It's never struck me as being greatly elegant from a software perspective, but I assume there are some good electrical engineering reasons. (And I'm intrigued about the mention of microcode... I always assumed that came later in the x86 series.)
As for the 8086 instruction set, it's kind of a jumble due to the need for backwards compatibility (of a sort) with the 8080. The 8086 instruction set doesn't make things easy for the chip implementation. Intel tried to make a nicer instruction set (with the i432 and then Itanium) but it didn't work out.
The i432 instruction set was, most emphatically, not nicer for implementation. E.g. it had bit-aligned variable-length instructions.
Let me repeat that, because it's totally insane:
bit-aligned variable-length instructions[1]
I knew quite a few people who worked on the i432 implementation, and architectural decisions like that nearly drove them insane.Intel top management at the time didn't have people who understood what was happening, and consequently let the i432 architects "play in the sand".
I always enjoyed that witty pun: "play in the sand" is what young children do in sandboxes; "sand" is mostly silicon dioxide and is often slang for what silicon chips are made of.
[1] https://en.wikipedia.org/wiki/IAPX432#The_project's_failures
I encourage people to read about the i432 processor, to get an idea of how wildly different a processor design can be. Among other things, objects are built into the processor. Object pointers are created by the processor, so you can't make an out-of-bounds pointer. (In other words, many current security issues don't exist.) The processor includes garbage collection, in hardware.
... and it's probably one of the least weird ultra-ciscy instructions that architecture has.
All the chips have a lot of internal structure to their opcodes, since certain bit fields specify the ALU operation, register, etc. Overall, I'd say the 6502 is the most structured, both because its instruction set is simple and because they didn't have backwards-compatibility to worry about. The 8086 has a lot of special-case instructions wedged into random spots, as well as a lot of inconsistency. For instance, sometimes bits 5-7 specify the register, sometimes bits 2-4, sometimes bits 3-4. And then there are the multi-byte "group" instructions tossed into the mix.
* one byte with implicit operands, for example LOOP or RET or ADD AX,imm
* one byte shortcut encoding with registers in bits 0-2 of the only opcode byte, these are 16-bit instruction only: INC/DEC, PUSH/POP, XCHG AX,rr (these instructions are also available as 2-byte encodings)
* two byte with one register and one register/memory operand, both encoded in the second byte (bits 3-5 and 0-2/6-7, with bit 0 of the first byte determining the size and bit 1 of the first byte determining if the source is the register or the register/memory operand)
* two byte with a single register/memory operand, in which case bits 3-5 of the second byte are part of the opcode
Prefixes and immediate operands complicate things a bit but that's it. It's refreshingly simple compared to the 386 and later.
An interesting tidbit is that registers are ordered AX/CX/DX/BX in the instruction set encoding because their function roughly matches AF/BC/DE/HL in the 8080 (BX comes last because like HL it can be used to address memory, even though the encoding of memory operands is completely different on the 8086)
You had to do e.g. “rep cs: movsb” instead of “cs: rep movsb” because the latter would switch to ds: on return from interrupt. Fun times.
An interesting story related to rep movsb was how the original Pentium-based Larrabee supported gather/scatter, as described in this slide deck from Tom Forsyth: http://tomforsyth1000.github.io/papers/LRBNI%20origins%20v4%... (gather/scatter info on slides 48-61, important background about virtual memory/pipeline in slides 21-28)
A summary:
The original Pentium had a very simple pipeline that could only access one cache line per instruction, and had to determine if there was a page fault early in the pipeline. This presented a problem for the Larrabee design, which needed to support gathers/scatters (which are vectorized loads/stores, and can read/write up to 16 different cache lines in one instruction). Additionally, the Pentium microcode system was quite slow, and gather/scatter performance was very important for many workloads.
To solve this, they looked at how rep movsb (which can read/write an arbitrary number of cache lines) works: as it executes, rep movsb modifies the cx, si, and di registers in place (or their 32/64-bit counterparts). This side effect actually helps keep the implementation simple, because if an interrupt occurs in the middle of the instruction, the registers keep all the state needed to continue executing after control returns to the instruction--it just keeps copying from where it left off.
So gathers/scatters used the mask registers from the Larrabee vector instructions to keep state: a bit in the mask indicates that a vector lane still needs to be read/written. The instruction is then run in a manual loop (not a microcoded rep prefix) until all bits in the mask are cleared. Interestingly, this implementation can be faster than gather/scatter in a modern AVX-512 out-of-order core: whenever multiple vector lanes point to addresses in the same cache line, those loads/stores can all execute at once; in contrast, in the big cores gather/scatter are split into one load/store micro-op for each lane, that each execute independently. And the masking isn't taken into account before the uop split, so gathering with only a single address unmasked can still take more than 16 uops (see https://mobile.twitter.com/trav_downs/status/122333322625875...)
The lowly Cortex-M0 exposes the progress of load/store multiple instructions in architectural state so they can be restarted where they left off after an interrupt for example. They even do this with the multiplier too, so if you have the slow but tiny 32 cycle iterative multiplier in your design, you can still get single cycle interrupt latency.
The M68ks had a halfway mechanism where it would just barf up partially documented internal microcode state onto the stack in a chip version specific manner on exceptions in the middle of instructions so you didn't restart the full instruction. Probably the grossest thing about that architecture.
The 68000 didn't do this, but couldn't restart a page fault.
The 68010 didn't either (but could restart a page fault).
The 68020 and 030 did do this horrible thing - doing Unix recursive signals was pretty hard if not impossible. And you couldn't copy this stuff to the user stack because it wasn't documented and so therefore you couldn't validate it when you pulled it back into the kernel.
The 68040 was sane again (and I presume subsequent 68ks)
Really this is part of the CISC vs. RISC thing - RISC instructions tend to have 1 side effect only, either they run to completion, or not at all, but CISC instructions can have multiple side effects - consider the infamous pdp11 instruction "mov -(pc), -(pc)" 3 side effects - 68k instructions are more complex multiple memory indirects, many possible faults, all that crud on the stack represents half done stuff
I don't know if the following is also the case for 8086 but recent cpu's have seperate decoding units for sub 4/5 bytes instructions. I would be interested in how it is known that an instruction is able to be decoded by those units of which multiple exist instead of the single full 15 byte decoder
For something slightly more recent, the book "Modern Processor Design" has 40 pages on the microarchitecture of the P6 processor. In brief, the P6 has three decoders running in parallel. An Instruction Length Decoder determines instruction boundaries before decoding, so the decoders receive aligned instructions. Only Decoder 0 can handle complex instructions that require microcode. It can issue 4 micro-ops per cycle, while limited-functionality Decoders 1 and 2 only issue 1 micro-op. The micro-ops are queued with up to 3 micro-ops issued per cycle for execution.
In terms of decoding instructions, there’s a stage in the pipeline which estimates the length of the (variable length) instructions and then passes is through to the next stage, which will then try and parse it. A failure here may cause a pipeline stall. Then there are other caches (like the loop stream decoder) which will bypass the instruction parsing for short loops, provided that the start of the loop is aligned to 16-bit. Processors can issue up to 4 wide (5 on recent intel CPUs) but some instructions may only be parsed by the first decoder. ISTR there’s more detail in Agner’s optimisation guides on this.
https://www.infoq.com/presentations/microarchitecture-modern...
Automatically computing parity is very important if you're implementing a terminal, since serial communication often uses a parity bit. The Datapoint 2200 was a programmable terminal / desktop computer introduced in 1970. Earlier than the microprocessor, it had a processor built from a bunch of 7400-series TTL chips.
The Datapoint company asked Intel and Texas Instruments about replacing the board of chips with a single chip. Intel eventually produced the 8008 to duplicate this functionality (including the parity flag), while Texas Instruments produced the TMX 1795 chip. Meanwhile, Datapoint redesigned their processor board around the 74181 ALU chip and decided they didn't want a microprocessor.
Texas Instruments couldn't find a customer for the TMX 1795 and abandoned it, but Intel decided to sell the 8008 as a general-purpose microprocessor, essentially creating the microprocessor market. This led to the Intel 8080 and then the 8086 processors, which because of backwards compatibility kept the parity flag (and the little-endian architecture). So that's why you have the near-useless PF flag.
I’d be interested in a compare and contrast to a MOSIS or UoU IO PAD cell.
http://pages.hmc.edu/harris/cmosvlsi/4e/lect/lect23.pdf
Oh ya, love the articles! It’s very interesting to compare an older successful design to more modern techniques.
P.S. Are there multiple clock domains in a 8086? If you could find a clock synchronizer or a level shifter that would be very interesting too.
P.P.S. Did you find any DFT features? Was DFT even a thing in the 70s?
The pins have a variety of implementations, depending on if they are tri-state, input only, output only, bidirectional, and other factors.
The 8086 has a two-phase clock. It's all one clock domain. The asynchronous input pins (I think reset and interrupt) have clocked latches to synchronize them.
What's DFT?
They have a resistor to ground in parallel with the buffer? I get the diode but I can only assume the resistor is a weak pull to prevent charge building up at the pin. But it seems the diode would mostly do that job, to the point of preventing damage anyway. I guess they weren’t concerned with leakage power then.
Protection resistors in series with the buffer are fairly common these days. Especially on GPIO type pins.
The resistor is just some silicon, probably around 30 ohms.
http://www.eng.biu.ac.il/temanad/files/2017/02/Lecture-9-IO....
I went so far as asking the chief engineer of the 386, but only got a short answer back confirming that it is “reserved”. The closest I got to a promising answer was when talking with the maintainer of os2museum.com, who, based on pre-release data sheets of the 386, speculated that it may have been a control register for the removed on-chip cache. But I’m still wondering if accesses to CR1 are just hardcoded to be undefined instructions, or if there is something somewhere.
Thank you for the great series! I’ve been a long time reader, and the 8086 and its more immediate successors have a special place in my heart.
How can I go from only knowing the basics to being able to reverse-engineer a schematic from a die photo, as in this article and this recent tweet [0]?
http://www.righto.com/2016/12/die-photos-and-analysis-of_24....
The 8 orange pieces are polysilicon, forming "bootstrap capacitors" which improve the signal levels when driving the instruction decode PLA below it. That was enhancement-load PMOS, so I guess by the time of the 8086 when depletion loads were already common, the bootstrap had fallen out of favour and superbuffers became preferred instead.
>"The transistors have complex shapes to make the most efficient use of the space."
What are some of the constraints and factors that go into deciding the individual shapes here? Does each Transistor have to meet a minimum width or gate length?
>"The dynamic latch depends on a two-phase clock, commonly used to control microprocessors of that era."
I thought this was interesting. When exactly did chip makers move away from using two separate clocks?
As far as clocks, four-phase clocks were popular for a short time earlier. I don't know details of the clocks in modern processors, so maybe someone can comment.