Part of the fun of working for a development tool company is that you get to peek into other people’s designs when they go wrong. Often we would get a call when the customer thought the source of the problem was the tool itself.
Such a case occurred when a company that makes an embedded controller for a piece of heavy equipment approached us and complained, “I have a problem in my code, and when I went to install your in-circuit emulator, things got worse instead of better.” This was before processors commonly had JTAG ports for emulation.
The target processor was an embedded variant of the Intel 80386. The problem the user faced was that he had a bug that he knew was well into his own code execution, but he couldn’t reach that bug to get it out using our emulator. To him, the emulator itself seemed to cause the code to run off into the weeds. The bug was in the same spot every time he attached it.
The spot in question where the emulator consistently “failed” was the point at which the x86 processor made the jump from “real” mode to “protected” mode execution. The reset vector of the x86 was always “high” in memory, usually up near “all Fs” in address space. The processor begins execution in “high memory” in a backwards-compatible mode (“real” mode) for addressing memory and executing code. Because “real” mode is limited in its ability to page in and out of memory and implement multi-tasking, it was superseded with the introduction of the 80286 processor by what’s known as “protected” mode.
“Protected” mode allows for all of these additional safety features related to protected memory space once the user’s code has awakened in “real” mode and set up a few key variables in a “descriptor table” to allow it to shift gears. It then sets the bit that transitions between the two modes. But the ancient appendages of the x86 architecture seem always to be with us, so “real” mode (a vestige of the 8086 processor) follows us around to this day, even in advanced chips such as the Pentium.
Usually the transition process, seen from the point of view of where code is executed, involves starting at an address very near the top of memory, running a few instructions from code space very high in the memory map, and dropping down slightly in memory to finish setting up the descriptor table. It then makes a large jump down to low memory when the processor itself shifts from “real” to “protected” modes.