Bochs: A Portable PC Emulator For Unix/X
By far, the majority of my time involving a computer was spent working with my Sun SPARCstation. I could do almost everything computer related, including e-mail interaction, system administration, net surfing, network Doom (I confess—it was me who circulated that hack so you could run Doom on Solaris 2.3), all on the same machine I used for software development. Occasionally, though, I found myself firing up the ol' PC on my desk, using MS Word to crank out documentation or a memo, or to run one of a handful of smaller utility programs I purchased years ago. Well, that got old quick! Having two keyboards, two monitors, and two computers on one's desk consumes more than desktop real estate—it takes much more time to maintain and introduces frustrations from the disparate OS designs. So, I began looking for a software solution which would allow me to run on my SPARCstation the handful of PC programs I used.
The idea of paying a lot of money to run something I'd already purchased didn't sit well with me, and I was interested in finding something I could extend if necessary. A little searching on the Internet didn't turn up anything low- or no-budget that would run MS Windows 3.1 on a SPARC and for which source code was accessible. The Wine and DOSEMU projects were making great progress, but they would never run on non-x86 architectures. There was an emulator program called pcemu, which would run on a SPARC. It did what it was intended to very well—run DOS. Unfortunately, it was engineered to emulate an 8086, and didn't lend itself to be extended to support 80286 and 80386 features. My search also turned up another 8086 emulator, one which went along with an older version of MINIX, allowing MINIX to run on non-x86 platforms. Since it was geared toward allowing a real mode version of MINIX to run within the emulator, this didn't seem like a good candidate for enhancing either.
There just wasn't anything out there that would do what I wanted it to do or which would be a good starting point if I was willing to put the effort into enhancing it. At the same time, I noticed significant similar interest on the net, specifically in the comp.emulators.* newsgroups. At that point, I began working on Bochs (pronounced “box”), a portable software PC emulator project, with the goal of making it possible to run PC software on a Unix workstation.
Looking back, it's still hard to believe it all came together. The biggest hurdle often was documentation. Here we have a somewhat undocumented DOS (and later Windows) trying to use parts of a multitude of non-standardized and sometimes poorly documented BIOSs, using hardware devices which aren't always documented accurately or fully, and running on a non-open Intel x86 architecture, which has undocumented instructions and features (e.g., LOADALL). If it weren't for the likes of The Undocumented PC and Undocumented DOS, I would have quit a long time ago. The other large hurdle was (and will be) tracking down and fixing the bugs which seem nearly impossible to find in such a complex system. One wrong bit flipped here, and sometimes the ill effect shows up 10 million instructions later, when the wrong character is displayed on the screen! Bugs which depend on timings proved to be elusive, since they don't always show up, and timing is drastically affected by the debug print statements inserted in the code. I've had occasion to sift through debug files of some 50+ megabytes (which I refer to as stealth bug contrails), only to find I didn't print out the one piece of info I needed!
In the two and a half years I've worked on this software, Bochs has made it past some very important landmarks. The first was booting up MS DOS 5.0 to the A prompt! At that point, I had only very rudimentary text HGA (a monochrome video card) emulation and no keyboard support, so I had to hardwire keystrokes into the keyboard BIOS to get past the time and date prompts during boot and to run DOS commands afterwards. It wasn't pretty, but it showed there was potential.
I was then on my way to implementing the graphics mode of the HGA and basic keyboard support, in an attempt to get Windows 3.0 running in real mode. After many red-eye nights, I managed to get Windows 3.0 to boot up past the initial graphics banner screen. What a sight that was! I remember staring at the screen, soaking it all in, not daring to type any keys, fearing it would hang Windows. Indeed it did, and that's when I knew I was right on track...
Though I had designed the emulator to be 32-bit right from the start, at this point I had only 8086 emulation. It was time to take the plunge and implement the protection and memory management models of the 80286. Surprisingly, after only a few months, I had coded a large part of 80286 support and was able to get MS Windows 3.1 (and 3.0) running in standard mode (286 protected mode).
Though MS Windows and MS-DOS have been largely the software I focus on running within the emulator, I've had some other interesting experiences. Recently, I've been working with the MINIX project (Andrew S. Tanenbaum and friends) to get it to boot/work under the Bochs emulator. The idea is to allow MINIX (a Unix clone suited for learning how internals work) to run on platforms to which it has not been ported. Recently, I was able to perform a full MINIX installation within Bochs and to boot up MINIX in 286 protected mode (although more work needs to be done).
I worked with and integrated my source code with the DOSEMU project's source, for a while. However, they were so productive with their code changes, it was hard to keep up, especially before things began to solidify with Bochs, and I had soon written my own keyboard and video support.
When I get to 80386 emulation (I've recently began this trek), I look forward to talking with the Wine team to look at the potential of a compile-time, optional merge of Wine and Bochs, allowing Wine to run on non-x86 platforms. Wine contains an MS Windows-to-X Windows/Unix translation technology, but it lacks x86 emulation which would allow it to run on other platforms. A merger would mean a significant performance gain could be achieved while running Windows within Bochs, since the Windows GUI and OS calls would be mapped to native Unix/X functions by Wine, instead of Bochs emulating the whole process entailed by the the Windows call. This is the same basic concept behind Sun's Wabi product.
Since the term “emulator” has been tossed around quite a bit in recent times, let me clarify the different types of emulation. Software such as DOSEMU and Wine are, in my terminology, environmental or OS emulators. That is, they run the executables natively (x86 code running on an x86 processor) and translate or “emulate” the interaction between the OS the executable expects (like MS Windows) and the OS that is currently running (like Linux). This kind of “emulation” is bound to an x86 platform, since no instruction set emulation is provided, but it is very efficient and an excellent alternative to having a dual-boot setup on your x86 machine.
Bochs is a pure emulator in that it emulates each x86 instruction in software, along with the necessary BIOS and hardware you expect to find on a PC. There's one giant decode loop, which closely models the fetch-decode-execute actions of the CPU. Components of the CPU—namely the registers—are modeled by fields in a large structure representing the CPU. Main memory is represented by a large array of memory within the C program. Input and output devices, such as the keyboard, timers, PIC, etc., are of a very modular design, “plugging into” the rest of the code via the registration of their corresponding IRQ, I/O address space usage, and interrupt service routines.
Bochs needed to emulate a graphics card supported by Windows, and I chose the monochrome Hercules Graphics Adapter (HGA), since it was the simplest to implement. Using an X window to represent the PC monitor and video frame buffer was a portable and natural choice, and it provided a keyboard input mechanism, as well.
Since all instructions are emulated in the C language, Bochs is not bound to any particular processor and is very portable to many other Unix platforms. I want to extend this theme as far as possible, and would like to see Bochs ported to other non Unix-like OSs, like Mac OS.
As with any emulation software, performance is always an issue, as running programs under an emulator is inherently slower than running native software. I've tried to keep performance in mind while coding Bochs, though I am postponing some optimizations until later, so that they don't interfere with the development path towards 386 emulation.
A few people have asked me if I had any plans for adding dynamic compilation techniques to Bochs to enhance the performance. I strongly subscribe to the K.I.S.S. philosophy, and I'd like to keep the source simple, understandable, and manageable, so I doubt I will add it any time soon. There is room in the immediate future for some more traditional performance enhancements, however. I think flags processing is an area with potential performance improvements. Though, one of the blessings of using such a simplistic (and brute-force) method of emulation, devoid of many fancy optimizations, is there are no problems handling self-modifying code, since the instruction stream is decoded and executed on-the-fly.
From the beginning, I coded Bochs to be a 32-bit emulator (386 and up). Internally, registers and other features have always been represented by 32-bit quantities. This has been an immense help, as I continue to add 32-bit processor features to the emulation support.
Since I began use of the GNU “autoconf” utility, the configuration and compilation process has become much easier and more portable. However, the process of setting up the environment (hard disk and floppy image files, VGA font, etc.) and the installation of DOS/Windows 3.1 within Bochs is a little lengthier. Consult the INSTALL file within the Bochs distribution for details. However, to give you a feel for the compilation and configuration process, I'm including the commands I use to compile Bochs and install Windows 3.1 on my Linux machine. Note, I'm using tcsh, and commands may vary on different platforms.
First, unpack the distribution:
$ cd parent-dir
$ gzip -dc bochs-YYMMDD.tgz | tar xvf -
Then configure the Makefile and source code, and compile.
$ cd bochs-YYMMDD
$ setenv CFLAGS "-O3 -Wall -m486"
$ ./configure --enable-80286 \\
--enable-native-floppy
$ make
If you are using a Bourne shell, such as bash, setenv will not work. Use
$ CFLAGS="-O3 -Wall -m486"; export CFLAGS
instead.
You may need to install the “VGA” font. Read the INSTALL file within the bochs distribution for this procedure. Your font directory may be /usr/lib/X11/fonts/misc, /usr/openwin/lib/X11/fonts/misc, or somewhere else.
$ cp fonts/vga.pcf font-dir $ mkfontdir font-dir
Create a bootable 1.44M floppy containing the DOS FORMAT and FDISK executables, using a real DOS machine. Assuming the 1.44MB floppy is drive A:
C:> FORMAT /s /u A: C:> COPY FORMAT.COM A: C:> COPY FDISK.EXE A:
Now insert this floppy into your workstation, and create an image of it as a Unix file. Assuming your 1.44MB floppy drive is /dev/fd0, do this:
$ dd if=/dev/fd0 ibs=512 of=1.44
Create an empty 1.2M floppy image file for B: (unused in this example):
$ dd if=/dev/zero of=1.2 bs=512 count=2400
Now create a 20MB hard disk image file (other sizes are possible, but we'll stick with a simple example for now):
$ dd if=/dev/zero of=20M bs=512 count=41820
Edit the .bochsrc file included at the top level of the source. Change the floppya line to floppya: file=./1.44, change the floppyb line to floppyb: file=./1.2, and change the diskc line to diskc: file=./20M.
Boot Bochs with the 1.44M floppy image file:
$ bochs -bootA
Bochs should now be running. Press the Return key a couple of times and accept the date and time.
Use FDISK to partition the C: drive (the 20M file). Add the whole drive as a PRIMARY partition. That is, run FDISK, and type Return three times; once to “Create DOS partition or Logical DOS Drive”, once to “Create Primary DOS Partition”, and once to chose the maximum size.
At this point, just click a mouse button on the window to exit bochs. Since Bochs doesn't support rebooting yet, you must exit and restart Bochs, then format the C: drive and add the DOS system files to it:
$ bochs -bootA A> FORMAT /u /s c:
Again, quit Bochs by clicking any mouse button in the window.
Now change the floppya line in .bochsrc to floppya: 1_44=/dev/fd0. Put Disk 1 of the Windows 3.1 installation set in the floppy drive. Then restart Bochs, booting from the hard drive image file, and fire up the Windows 3.1 installation:
$ bochs -bootC C> a:setup
Press the Enter key to install Windows now, and press. “c” to do a custom installation. Press Enter again to install Windows in the default C:\WINDOWS location.
In the Windows Setup screen, everything should be recognized fine, except the keyboard type, which you will need to change. Use the arrow keys to select “All AT keyboards”. Continue installing Windows, using keystrokes only (remember, clicking the mouse in the window exits Bochs). There is no point in setting up printers and applications. Allow Windows to create (or change) your AUTOEXEC.BAT and CONFIG.SYS files, and watch Windows boot. Then return to DOS, click in the window to exit Bochs, and restart Bochs and Windows:
$ bochs -bootC C> WIN
Figure 1 shows a sample screen. Note that there are some artifacts of bugs in the display code showing.
The set of hardware, CPU, and BIOS features supported by Bochs has been largely determined by the suite of software which I've run within the emulator environment. A lot of my effort has been focused on supporting enough features to run MS DOS 5.0 and Windows 3.1. Recently, I've added Minix 1.7.2 to my list of targeted software. Given this, Bochs currently supports the following items: (Keep in mind that new features are always being added, and some of these may only be available in the next release):
8086/80286 instruction set including protection and virtual memory models. An option to the configuration script allows for compiling for either architecture.
Monochrome Hercules Graphics Adapter (HGA) This is a fairly simplistic graphics adapter with character and pixel mapped graphics capabilities. Text and pixel graphics are mapped to an X window.
Floppy drives: 1.44M 3.5", 1.2M 5.25", and 720K 3.5" These are implemented by mapping them to either Unix files of the corresponding size or to the floppy drive on your workstation. Originally, only BIOS software interrupts (like a subroutine call) were supported, but recently I added a minimal amount of Direct Memory Access (DMA) and floppy I/O support for use with Minix.
Hard drives: 10, 20, or 30-Megabyte drives Hard drives are also implemented as a Unix file. Read and write requests to the hard drive are directed to read() and write() Unix system calls, operating on the hard disk image file. Access to the hard drive is currently limited to BIOS software interrupts. It's likely that use of DMA and hard drive I/O will be added.
Master/slave Programmable Interrupt Controller (PIC) Hardware devices which need to communicate their need to interrupt the CPU to be serviced are attached to either PIC. These include the keyboard, system timer, disk controller, etc. A fairly complete emulation of both master and slave PIC is provided.
Non-enhanced keyboard Keystroke input from the X window used for the video display, provides input for the keyboard emulation. Both BIOS keyboard software interrupt (int 16H) and hardware I/O access are provided. The keyboard code still needs a little work.
CMOS functions I've implemented only a partial set of CMOS functions, although it allows both BIOS software interrupt and hardware I/O access. Primarily, functions which deal with CMOS time, date, and shutdown status are provided. A CMOS real-time clock (RTC) has not yet been implemented.
A fair amount of typical BIOS interrupts and data area values These functions include sensing features and capabilities of your PC, memory size information, reading/writing floppy and hard disk sectors, an interval timer, video functions, a bootstrap loader, etc.
Makefile configuration via GNU 'autoconf'
Runs DOS 5.0, Windows 3.0, Windows 3.1 Additionally, I was able to install and boot up Minix 1.7.2, but more work needs to be done.
There are many more features to add to Bochs to truly make it a useful tool. In the near future, the plan is to focus on adding the 80386 features set, since this will allow Bochs to run up-to-date operating systems, such as Windows 95 and 32-bit code within Windows 3.1. I have other improvements in mind, and I get more ideas from suggestions and input I receive via the Net. Here's a list of additional features I have planned:
386, 486, and other x86 generation features There's a good start on 32-bit emulation already. Coding should progress nicely; testing and debugging will be slower and more arduous. This should allow Bochs to run Minix for 386, Windows 3.1 in enhanced mode, Windows 95, and Linux.
Networking
Floating point coprocessor emulation
Mouse emulation I've already got a good start on emulating a PS/2 mouse, but since it's so intimately integrated with the keyboard hardware, it makes debugging the keyboard/mouse code difficult. Mouse code is currently disabled until I revisit it. I'd really like to get good (low-level) documentation on a Logitech or other bus mouse, in which case I'd happily implement it.
File system redirector This would allow DOS or Windows to map a drive, such as E:, to your Unix directory (e.g., /home/johndoe/windows-drive). You'd also be able to read CDs mounted on your Unix file system.
More performance enhancements I'm hesitant to do much in the way of performance enhancements until 386 features are coded, but afterwards, a lot of elements of the code need to be examined for potential performance enhancements. This will be a good time to elicit ideas from the performance wizards of the world.
And more...
For further information about Bochs, the developer's e-mail list, FTP site, etc., please visit world.std.com/~bochs/.
Kevin P. Lawton (bochs@world.std.com) is an '88 Computer Science graduate from the State University of New York at Oswego. He worked for MIT Lincoln Laboratory for 6 and 1/2 years as a software engineer and systems administrator. His off-line interests include mountain biking, skiing, hiking, Ultimate Frisbee, and just about anything else involving adrenaline and the outdoors. find more info about bochs at world.std.com/~bochs/.