One of the things I’ve always liked about Intel's 32-bit processors is their built-in hardware-protection mechanisms. Sure, any processor can run your C code. But how many can catch programming errors at run time? Automatically, and with practically no software intervention? It’s nice to have a chip that’s got your back.
What I mean is, every x86 from the ’386 onwards has at least three hardware safety nets: the memory segmentation, the privilege hierarchy, and the built-in task management. (There are others, too, but that’s for another day.) All three catch errors in my code – or other peoples’ code – without any help from me. I can’t think of another CPU family that has so much built-in safety checking.
First, there's memory segmentation. Every old x86 programmer knows about this. Code is kept separate from data, data is separate from stacks, stacks are separate from code, and so on. In the old days it was a nuisance, but now it helps keep everything Kosher and avoids intermixing dissimilar blocks of memory. That keeps you from accidentally jumping into the middle of your stack space, for example, or from writing variables into the middle of your code.
Again, in the old days, segments were fixed size. Now you get to tell the chip how big each of those segments is, and it will automatically prevent you from running off the upper or lower end. For instance, if your code segment is exactly 0x34FF0 bytes long, the processor will absolutely refuse to fetch instructions from address 0x34FF1. Nor will it allow you to “underrun” the code segment by fetching from a lower address. You can have as many code segments as you want, and each one can be any arbitrary size. The chip silently protects them all. It’s like automatic run-time bounds checking, but without the overhead.
The second bonus is the privilege-level protection mechanism. Every code segment gets assigned (by you) one of four privilege levels. Lower-privileged code can call higher-privileged code only through defined “call gates,” a special type of jump/call instruction. That prevents untrusted code from usurping the advantages of more trusted code. It’s a great way to separate the operating system from the drivers, middleware, and applications. I generally assign the lowest privilege level to anything that’s downloaded or that didn’t come straight out of ROM, just in case.
What’s nice about Intel’s privilege protection is that it’s baked into the silicon, so you can’t hack it, spoof it, or recode it. It’s a fundamental part of the chip and can’t be disabled or sidestepped. (You can assign all your code to the same privilege level, if you like, effectively neutralizing the protection, but that’s your decision.) This built-in mechanism is a lot more solid than the software-based equivalents you see in most operating systems.
Finally, there’s the built-in task-management feature, which I think is really underrated and underused. Most embedded systems have tasks, but most of them use some sort of software task scheduler or RTOS to manage them. Fair enough, but did you know that your x86 processor can probably do that for you automatically, in hardware, for free?
You can tell the chip what segments of code, data, and stack comprise a “task,” and you can have as many tasks as you want. When it’s time to switch tasks (on a timer interrupt, for example), the processor will – all on its own – stop executing the current task, freeze its state and all of its context, load the new task, and start executing from where it left off. All of this is done in hardware, with virtually no software overhead to speak of. It’s remarkable. Once you set up all the parameters and press the Go button, it’s all automatic.
You get the idea. There’s a PDF on all this stuff here. It took me a while to get the hang of it all, but it paid off in catching both sloppy programming and more subtle bugs. I'm a fan.