Bochs: A Portable PC Emulator For Unix/X
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
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.
- Bruce Nikkel's Practical Forensic Imaging (No Starch Press)
- Transitioning to Python 3
- Progress on Privacy
- Stepping into Science
- Linux Journal December 2016
- Radio Free Linux
- The Tiny Internet Project, Part II
- CORSAIR's Carbide Air 740
- FutureVault Inc.'s FutureVault
- A Better Raspberry Pi Streaming Solution