Kent's Blog
EC Container 5

As If

I thought I’d start with is the “As If” principle. Many hardware and software requirements are written implying a particular implementation. But you don’t have to do that. You just have to implement behavior “as if” you had done that.

A good example of this principle is CPU design and in-order execution. Every general purpose CPU I can think of maintains the illusion that each instruction executes in order serially, at least for single-threaded code. Modern CPUs generally do out-of-order execution, involving purposefully executing instructions as early as possible, and then fixing things up so that the instructions appear to have executed in order. But even before that, CPUs were prefetching or doing “hit-under-miss” and other things to speed up execution by not doing things in a strictly in-order way.

This applies to software as well. For my Apple 2gs emulator,
KEGS, I wanted to implement accurate emulation of video and sounds. KEGS emulates a machine released in 1986, so the machine design isn’t that complex. Software running on these old machines would often change the display mode while the CRT was still drawing the screen. To emulate that properly, one solution is to act like the real hardware: execute one instruction, then call the video update routines and draw a pixel or two, and repeat. This is very slow--a lot of work is done for every instruction emulated.

But KEGS takes a different approach. Instead of drawing pixels after each instruction, KEGS just keeps tables describing what it should do, and does the video update for the whole screen all at once. For example, video mode changes do nothing right away, other than fill in a table noting the mode changed on line 62. When the video refresh routine is called, it then calculates the new pixels as needed on the lines which are now being shown in a different mode than they were last refresh. The pixel memory is tracked with change bits--any write to video memory also sets a bit indicating an 8-byte region has been written to. The refresh routines only redraw pixels which changed. So video memory writes are only slightly slower than other memory writes (they have to also set a changed bit), and yet the refresh routines don’t have to look at every byte in video memory to determine what bytes changed. This isn’t a perfect example since I suspect a different approach would be even faster for 2GHz host processors, but this was a great solution when my host machine was only running at 60MHz. This lets KEGS behave almost as if it updates the video display after every instruction, at almost no slow down in emulated CPU instruction speed.

Implementing “as if” sometimes is a trade-off of complexity for speed, and my two examples show that tradeoff. A lot of CPU complexity goes into making it appear as if instructions are executed in order, and KEGS adds data structures to track delayed video updates.
EC Container 6