Aeon Emulator Blog

October 15, 2009

Modeling a PC

Filed under: Aeon — Greg Divis @ 9:08 pm

In addition to emulating the processor, there’s a lot of other components required to run a program written for DOS. You can get by running some very simple applications emulating only the CPU, RAM, and a small subset of DOS, but to run anything more complicated you have to also deal with the interrupt controller, programmable timer, VGA card, BIOS functions, and keyboard. In Aeon, the class responsible for modeling this system of devices is VirtualMachine.

In VirtualMachine’s default constructor, all of these essential components are initialized in a way that leaves its state pretty close to a real MS-DOS PC after you’ve booted it up. At this point, certain core hardware elements are exposed as read-only fields, while devices with more abstraction are exposed via properties and methods. The use of read-only fields was mainly because Aeon relies on a significant amount of dynamically generated IL, and it just made things easier; these hardware elements are pretty fundamental and tightly-coupled with each other, so I don’t think it’s all that unreasonable from a design perspective either. Most external assemblies referencing Aeon will have no need to do things like examine or modify the state of the emulated CPU registers, but there are some cases where this is important.

The following code illustrates what you need to do to get a very simple emulated system up and running using a VirtualMachine instance:

static void Main(string[] args)
{
    // Create an instance which maps C:\Games\DOS\Zork1 on the host computer to C:\ on the emulated system.
    // The final parameter indicates that this is not read-only (DOS is allowed write to this directory).
    var hostPath = new HostDirectory(@”C:\”, @”C:\Games\DOS\Zork1″, false);

    // Create the emulated system.
    using(VirtualMachine vm = new VirtualMachine())
    {
       
// Add the previously defined mapping to the VirtualMachine’s list of file system root paths.
        vm.FileSystem.Roots.Add(hostPath);

        // Read an executable file to a ProgramImage instance that Aeon can understand.
        var program = ProgramImage.Load(@”c:\_zork1.com”, vm);

        // Loads the ProgramImage as if it had been run from a DOS command line.
        vm.LoadImage(program);

        // Run forever!
        while(true)
        {
           
// Decode and emulate the instruction at the CPU’s instruction pointer.
            vm.Emulate();
        }
    }
}

That’s technically all you need to do to emulate Zork, but you won’t get any output displayed on your screen, or be able to provide any input to it. Not terribly useful unless you’re running a non-interactive program that just reads and writes from the disk. Still, even if I had provided redirection of the emulated console input/output streams, the only programs that will work with this configuration are ones that do not patch interrupt vectors. VirtualMachine requires you to manually test for hardware interrupts, then raise the appropriate software interrupt using its RaiseInterrupt method. Also, you have to manually generate hardware timer interrupts from the programmable timer device, and of course gracefully break out of that while(true) when there’s nothing left to do.

To simplify setting up emulation of a more complicated (and complete) system, you can use the EmulatorHost class instead, which wraps a VirtualMachine and creates a dedicated CPU emulation thread to take care of everything I mentioned above:

static void Main(string[] args)
{
    // Create a class which maps C:\Games\DOS\Zork1 on the host computer to C:\ on the emulated system.
    // The final parameter indicates that this is not read-only (DOS is allowed write to this directory).
    var hostPath = new HostDirectory(@”C:\”, @”C:\Games\DOS\Zork1″, false);

    // Create the emulated system.
    using(EmulatorHost emulator = new EmulatorHost())
    {
       
// Add the previously defined mapping to the VirtualMachine’s list of file system root paths.
        emulator.VirtualMachine.FileSystem.Roots.Add(hostPath);

        // Loads the ProgramImage as if it had been run from a DOS command line.
        emulator.LoadProgram(@”c:\_zork1.com”);

        // Start running the loaded program in the background.
        emulator.Run();

        // Run until the program terminates.
        while(emulator.State != EmulatorState.ProgramExited)
        {
            System.Threading.Thread.Sleep(100);
        }
    }
}

It may not look like a huge difference, but this code snippet actually implements a complete system running Zork, complete with hardware interrupts being generated in the background. Typically, a more sophisticated emulator would subscribe to events on VirtualMachine or EmulatorHost to handle things like video mode changes, mouse movement, and other runtime conditions. EmulatorHost also exposes methods like Pause, Run and Halt methods to control emulation and PressKey, ReleaseKey, and MouseEvent to put input events in the emulator’s hardware queues. Unlike VirtualMachine, EmulatorHost’s methods and properties are safe to use from other threads.

This post went on a bit longer than I intended, so I’ll end things right here. I’ll get to how this stuff is implemented eventually…

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: