Reverse-engineering the ModR/M addressing microcode in the Intel 8086 processor
righto.com
righto.com
There was some discussion about that here in the past, not surprisingly on another one of Ken's 8086 RE articles: https://news.ycombinator.com/item?id=24248773
There are other notable undefined ModR/M combinations here, which the behaviour thereof can hopefully now be answered: https://retrocomputing.stackexchange.com/questions/21747/und...
As for JMP FAR rm with a register, what I seen in the microdcode doesn't quite match the linked discussion. The microcode is
IND → tmpC INC2 tmpC JMP FAR rm
Σ → IND SUSP
tmpB → PC R DS,P0
OPR → CS FLUSH RNI
So it will add 2 to the hidden IND register, read the segment value from that address, and put that in the CS register. Meanwhile, the PC gets the value from the hidden ALU temporary B register.As for the mystery FF FF instruction, I think it jumps into "PUSH rm" and ends up pushing DI.
SP → tmpA DEC2 tmpA PUSH rm
Σ → IND
Σ → SP
M → OPR W SS,P0 RNIIn other words, it seems that FF/6 (the normal "PUSH rm") and FF/7 (officially undefined) are aliases? The other notable anomaly is that 8F/1-7 are also officially undefined, while "POP rm" is at 8F/0.
Ignoring the 0F-prefixed ones (POP CS, everyone knows that one by now), there are some interesting gaps in the group table:
https://www.sandpile.org/x86/opc_grp.htm
Perhaps all those undefined opcodes could be worth writing another detailed article about, now that you've become familiar with how the instruction decoding works.
Writing assembly for the 8086 was actually quite pleasant, the space of stuff you could do was broad and the expression was clean.
[1] Though not the same registers. The list of registers is populated differently for 8 bit and 16 bit instructions. The 386 then played the same trick AGAIN, but placed the selector bit in the code segment descriptor and did a weird dance with how to encode the ESP register (to flag a new "SIB" byte for extended modes), and made a giant hash of things that no one could understand. Writing assembly for the 80386 was definitely not pleasant, though it was actually an easier target for compilers.
On the other hand, the 64-bit addressing modes that AMD created with the 64-bit extension are a lot weirder.
The only weird part is that AMD has decided for some very stupid reason to not decode 2 of the 3 bits provided for register numbers in the REX instruction prefix.
Perhaps this AMD decision allowed them to obtain an extra 0.1 GHz in the clock frequency of the 2003 model of Opteron, but it has complicated forever the optimal register allocation on the Intel/AMD CPUs.
While on a 386, with respect to memory addressing (when segmentation is not used) there are only 3 kinds of general-purpose registers (SP, BP and all others), due to this AMD mistake in 64-bit mode there are 5 kinds of general-purpose registers (SP, BP, R12, R13 and all others), where each of the 5 kinds of registers has a slightly different behavior, e.g. resulting in different program sizes, depending on how the registers are allocated.
So yeah, serious domain experts and modern optimizers can do really well with the architecture, but it's a huge pain to try to "get stuff done" in, and it's from an era where serious Stuff was still gotten done in hand-coded assembly as often as not.
Basically: with a Z80 or 6502 or 8086, if you had a task "X" to do the "best" way was as often as not the obvious way. With the 386, that was suddenly no longer true and you had to have a bookshelf full of Abrash tomes and whatnot. It was a very different feel.
Do we really know whether fixing the Pentium FDIV bug would have actually been in the reach of microcode patching capabilities? Based on googling the FDIV bug is due to errors in some sort of PLA which presumably couldn't have been patched.
On an 80186+, you'd get a #UD, but the 8086 doesn't have strictly "undefined" opcodes; everything does something.
(And yes, I'm still waiting with bated breath to see the teardown of the machine that implements this ridiculous bus.)
So indeed, if nothing does anything, #READY will go active automatically, simultaneously with the data lines (not!) being driven, and the CPU will sample junk.
Which is to say: IBM had to add a bunch of hardware to the board just to synthesize a default/ignore behavior for this line, which wouldn't have been necessary had Intel done it right the first time.
On the 8086, the microcode counter would eventually roll over and reach a "next instruction" bit even if it went into a normally unreachable part, so I don't think that such a state is possible.