Reverse engineering the Intel 386 processor's register cell
righto.com
righto.com
I have been wondering for literally decades now.
The best theory I’ve read so far came from Michal (os2museum) when I talked to him: The 386 was supposed to have an on-die cache, but that was stripped out late. There are surviving pre-release data sheets documenting that. The idea is then that CR1 controlled that cache, but got ripped out with the rest of it.
If that is the case, there likely isn’t an actual CR1 register at all, and only the opcode encoding survived. But if there should be an actual register, that would be massive news.
But the theory sounds totally plausible: they'd have written up the ISA spec long before they started designing the circuits. So if CR1 got dropped early all the existing designs would still be targetting 0/2/3 explicitly.
But FWIW: the "why wasn't it ever used" question is easier to answer: the window was extremely short. The MOVCR instructions only supported 8 32 bit registers, and when the Pentium shipped that was already too small and all future CPU state extensions were done using the new CPUID and MSR mechanisms.
Though it’s mostly just a corollary to the existence of the completely reserved CR1 itself, which was added in the 386 at the same time as CR2 and CR3 (so I don’t think compatibility concerns apply). CR1 is entirely a forbidden zone between the well-defined CR0 and CR2. At the time, we thought that maybe CR1 was supposed to be the next register new control bits would be added into once the adjacent CR0 is full, but then Intel introduced CR4 for that instead, and made the whole CR1 gap more mysterious.
That Intel decided to skip CR1 entirely speaks of a story. Before I knew of the plans of having an on chip cache, this story was a giant mystery. Now that I know of the possibility that it might have been related to the late-scrapped plans of having an on-chip cache on the 386, I think that’s a pretty interesting potential reason already, but I’m not yet satisfied unless confirmed.
But if CR1 actually exists in the 386 in some form other than the encoding, then that's massive news.
Nonetheless the "official" means for adding state to an x86 is an MSR. And in particular there's no good reason for putting this in legacy spaces like CRs: CR8 is an interrupt control register, unrelated to the MMU control in CR0/2/3.
(In fact the proof of this is that within the decade, x86 interrupt handling needed to evolve in the direction of MSI's and x2apic, something that even the newly-expanded CR registers were completely inadequate to do. So we do that stuff in MSRs too.)
I understand that these memory cells needs both bit and #bit, but how are both provided to the cell? Seems like you'd need a lot of inverters around for those
THANK YOU!
I'm no expert...but I'd guess the 2 mystery registers are for scratch use by the microcode. When executing CALL, INT, etc. in protected mode, a '386 chip can easily burn 200-300 clocks on just that one assembly instruction. Lord knows how many corner cases there might be, when various things go wrong mid-instruction.
The 286 already had 8 "segment registers" internally: ES, CS, SS, DS, GDT, LDT, IDT & TSS. Only the first four are directly accessible by the program, but to the microcode they should all be more or less identical.
Besides the layout of the saved CPU state in memory, another clue to this is that for exceptions involving both "normal" and "system" segment registers, an error code gets stored in one of the internal registers, indicating which segment caused it. The only way to read it out in software is by executing the undocumented STOREALL instruction (F1 0F 04) immediately after the reset from a triple-fault shutdown.
The codes that appear in this register are:
6CFFh GDT
6DFFh LDT
6EFFh IDT
6FFFh TSS
70FFh ES
73FFh DS
CS and SS should be 71h / 72h, however exceptions involving these segments seem to take a different microcode path that overwrites that register with the access rights for CS.I have written about this here: https://rep-lodsb.mataroa.blog/blog/the-286s-internal-regist...
And this article shows the bus cycles done by LOADALL on the 386, note that it also loads 10 internal registers:
But wait, HIMEM.SYS used LOADALL to avoid going into protected mode and back?!? When you thought you knew everything (which I absolutely don't, but I knew it used big real mode on the 386 and I totally didn't expect LOADALL).
> More useful is the ability to load any arbitrary base address for the segment registers without entering protected mode. Some versions of Microsoft's HIMEM.SYS did this to copy data between real and extended memory.
It is also explained at https://www.os2museum.com/wp/himem-sys-unreal-mode-and-loada...
> physical implementation is a complicated than the theoretical