So here I am, writing a bit of test code to figure out the behavior of x87 FPUs with regard to saving and loading the FPU state (FSTENV/FLDENV and FSAVE/FRSTOR instructions in different modes and formats).
The original real-mode only 8087 state format included the instruction and operand pointers as 20-bit linear addresses (because $REASONS) and also stored 11 bits of the floating-point (FP) opcode; the remaining five were always the ESC instruction.
The 287 also needed to be able to save the state in protected mode, with full segment and offset addresses. For whatever reason, Intel decided to keep the size of the saved FP environment (7 words); because the saved code and data pointer addresses used 32 bits each instead of 20, there was no longer room for the floating-point opcode. That wasn’t a huge deal because the opcode could be fished out of memory.
When the 387 came out, it naturally needed to support 32-bit state, with 16-bit segments and 32-bit offsets. Instead of 7 words, the 32-bit state was extended to 7 dwords, with extra padding in reserved fields.
I was pretty sure the floating-point opcode is there somewhere in the 32-bit protected-mode state, but needed a reminder as to where exactly. I happened to have Agarwal’s 80×86 Architecture & Programming, Volume II (1991) open on a nearby page, so I looked there first. On page 240, there’s a diagram of the 32-bit protected-mode FPU state format. But no FP opcode. Odd.
The next closest book was Hummel’s PC Magazine Programmer’s Technical Reference: The Processor and Coprocessor (1992). On page 696, documenting the FSTENV/FNSTENV instruction, there’s the saved state diagram. But again, no floating-point opcode! Is my memory that bad? (Rhetorical question, don’t answer!)
Continue reading