Scott Hanselman

The Weekly Source Code 44 - Virtu, an Apple Emulator in C# for Silverlight, WPF and XNA

July 14, 2009 Comment on this post [8] Posted in Arcade | Open Source | Silverlight | Source Code | Windows Client | WPF
Sponsored By

Virtu.RasterBlaster I really advocate folks reading as much source as they can because you become a better writer by reading as much as writing. That's the whole point of the Weekly Source Code - reading code to be a better developer.

Reading code in Open Source projects is a good way to learn, especially if the project has been around a while and been successful, or if you already respect the team of people working on it. Less reliably, you can find snippets of code by searching and sharing code.

I love Emulators. They are magical. Earlier this year I interviewed Pete Brown when he created a C64 Emulator in Silverlight.

Now, it's Apple IIe time. From the Virtu Project Site, you can see that this source has been around in various forms for years...morphing from form to form.

Originally developed for RISC OS (3.11) on the Acorn Archimedes in 1995 using some C but mostly ARM assembly language. Published on the cover disk of the October 1997 issue of Acorn User. Later that year we started porting Virtu to Microsoft Windows (95) on the 'PC' using only C++ with DirectX. A port to Microsoft Windows CE (2.11) soon followed. These were tweaked over the next couple of years but never published. Fast forward to the present and the latest incarnation of Virtu, this time ported to the Microsoft .NET Framework (3.5 SP 1) using only C# with Silverlight, WPF and XNA (on both Windows and Xbox 360, which is limited to the .NET Compact Framework).

In this form, Virtu was written by Sean Fausett with some help from Nick Westgate. This code is interesting for a number of reasons. First, because it's a freaking AppleIIe emulator in a language I like to read (*cough* Not C *cough*), but also because it is cleanly structured and includes Silverlight (that means Mac also!), WPF and XNA (Xbox360) versions. It illustrates a way one can factor their code into an engine and various hosts.

IMPORTANT NOTE: To run, Virtu needs two files that are not included: An image of the standard or preferably the enhanced Apple IIe monitor ROM needs to be copied as 'AppleIIe.rom' (16 KB) to the Roms directory. An image of the Disk II (16 sector) interface card ROM needs to be copied as 'DiskII.rom' (256 bytes) to the Roms directory. You'll also need some disk in the form of a ".nib" file like RasterBlaster.nib, for example. I can't give you those files.

After a successful build, you should be able to run the emulator and perform a self test by pressing the hallowed key combination Control+OpenApple+CloseApple+Reset.

Looking at the WpfKeyboardService.cs, I can see how those keys I don't have are mapped to keys I do:

ModifierKeys modifiers = keyboard.Modifiers;
IsOpenAppleKeyDown = keyboard.IsKeyDown(Key.LeftAlt);
IsCloseAppleKeyDown = keyboard.IsKeyDown(Key.RightAlt);
IsResetKeyDown = ((modifiers & ModifierKeys.Control) != 0) && keyboard.IsKeyDown(Key.F12);

IsCpuThrottleKeyDown = keyboard.IsKeyDown(Key.F8);
IsVideoFullScreenKeyDown = keyboard.IsKeyDown(Key.F11);
IsVideoMonochromeKeyDown = keyboard.IsKeyDown(Key.F9);

Looks like that's ALT, ALT, CTRL, F12 which gives me a weird series of self test screens then "System OK" which is a good sign.

image

This is nice, now I can do a little Applesoft BASIC by booting to the monitor with Ctrl-F12 then typing this, then RUN.

10 TEXT:HOME
20 ?"HELLO WORLD"

Thrilling!

image

It's really fun code to read and it's a lot cleaner than you'd think for an emulator, although there's the expected Giant Scary Switch Statements here and there. Other parts definitely feel like they've been brought along from the past, although, how else would you do them? (Don't look in VideoData.cs, your face will melt.) For example, here's how they draw text (remembering that we're not using Fonts here, we've got a REALLY low res screen):

private void DrawText40(int data, int x, int y)
{
int color = Machine.Settings.Video.IsMonochrome ? ColorMono00 : ColorWhite00;
int index = _charSet[data] * CharBitmapBytes;
int inverseMask = (_isTextInversed && !_memory.IsCharSetAlternate && (0x40 <= data) && (data <= 0x7F)) ? 0x7F : 0x00;
for (int i = 0; i < TextHeight; i++, y++)
{
data = CharBitmap[index + i] ^ inverseMask;
SetPixel(x + 0, y, color | (data & 0x01));
SetPixel(x + 1, y, color | (data & 0x01));
SetPixel(x + 2, y, color | (data & 0x02));
SetPixel(x + 3, y, color | (data & 0x02));
SetPixel(x + 4, y, color | (data & 0x04));
SetPixel(x + 5, y, color | (data & 0x04));
SetPixel(x + 6, y, color | (data & 0x08));
SetPixel(x + 7, y, color | (data & 0x08));
SetPixel(x + 8, y, color | (data & 0x10));
SetPixel(x + 9, y, color | (data & 0x10));
SetPixel(x + 10, y, color | (data & 0x20));
SetPixel(x + 11, y, color | (data & 0x20));
SetPixel(x + 12, y, color | (data & 0x40));
SetPixel(x + 13, y, color | (data & 0x40));
}
}

In Silverlight, they use the same (only) technique that Pete Brown's C64 emulator used to use, the new WriteableBitmap class. This means the XAML is just a single Image, and everything is a dynamically generated Bitmap. Here's the SilverlightVideoService.cs:

namespace Jellyfish.Virtu.Services
{
public sealed class SilverlightVideoService : VideoService
{
public SilverlightVideoService(Image image)
{
_image = image;
SetImageSize();

_bitmap = new WriteableBitmap(BitmapWidth, BitmapHeight, BitmapPixelFormat);
_pixels = new uint[BitmapWidth * BitmapHeight];

Application.Current.Host.Content.Resized += (sender, e) => SetImageSize();
}

[SuppressMessage("Microsoft.Usage", "CA2233:OperationsShouldNotOverflow", MessageId = "y*560")]
public override void SetPixel(int x, int y, uint color)
{
_pixels[y * BitmapWidth + x] = color;
_pixelsDirty = true;
}

public override void Update()
{
if (Application.Current.RunningOffline && /*_window.IsActive &&*/ (_isFullScreen != IsFullScreen))
{
_isFullScreen = IsFullScreen;
}

if (_pixelsDirty)
{
_pixelsDirty = false;
_bitmap.Lock();
for (int i = 0; i < BitmapWidth * BitmapHeight; i++)
{
_bitmap[i] = (int)_pixels[i];
}
_bitmap.Invalidate();
_bitmap.Unlock();
_image.Source = _bitmap; // shouldn't have to set source each frame; SL bug?
}
}

private void SetImageSize()
{
Content content = Application.Current.Host.Content;
int uniformScale = Math.Min((int)content.ActualWidth / BitmapWidth, (int)content.ActualHeight / BitmapHeight);
_image.Width = uniformScale * BitmapWidth;
_image.Height = uniformScale * BitmapHeight;
}

private const int BitmapWidth = 560;
private const int BitmapHeight = 384;
private static readonly PixelFormat BitmapPixelFormat = PixelFormats.Bgr32;

private Image _image;
private WriteableBitmap _bitmap;
private uint[] _pixels;
private bool _pixelsDirty;
private bool _isFullScreen;
}
}

It's a nice codebase and fun to step through. If you're interested in learning about emulation, check it out.

There are Wiki pages with details and quirks for each platform, WPF, XNA and Silverlight. There's still work to be done, so you might head over there and offer to help!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
July 14, 2009 23:00
very cool stuff. I love how the pixels are actually separated like they would be on a real display.

Pete
July 14, 2009 23:02
Haven't had to do bitwise operations in a while, thank heavens.

Time to break out the GOTO's, I say!
July 15, 2009 1:04
That is awesome beyond belief. If only I had some way to read the old disks I have. I would love to see what kind of junk I was writing when I was 16!
July 15, 2009 7:58
This emulator makes it possible for MacBook Pro owners to work with an application that's running on a virtual Apple IIe that's running in Silverlight that's running in Firefox that's running in Windows that's running in VMware that's running on a Mac. I don't know if that's scary or awesome, but my mind is a little bit blown.

(Of course, you would not be able to work with an application that's running on a virtual Apple IIe that's running in Silverlight that's running in IE 6 that's running in Windows XP Mode that's running in Windows 7 that's running in VMware that's running on a Mac, since that would just be silly.)
July 15, 2009 12:53
Why are the ROMs not distributed with the project? - I know (some of) legal side - but the only IP of any value in this ROM is educating the current and next generation of programmers....
July 17, 2009 17:36
I have the same problem with this as I have with many awesome open source projects: lack of comments. A search through the WPF project reveals 695 comments. I'm not one to argue that any comments::lines of code ratio is a valid target, but I feel like a little extra effort could reduce the barrier to entry for new developers. Class, method, and property "this is what it does" comments aren't there on anything, let alone members that do something clever in the name of optimization. I'm staring at MachineEvents right now and all I can think about is how 3-4 sentences of comments would mean I don't have to hand-execute the code on paper to figure out what it's doing and compare that to what I think it's doing. I do have a compliment for the short comments in Memory.cs and Cpu.cs; those are very well done.

It's a shame that the only parts of the code that get comments are the parts that would be too hard for the developers to understand without comments. I know there's a "less comments" movment among many developers and I agree with many of its tenets, but I don't think "no comments" is a good path.

July 20, 2009 17:11
The video on the Apple IIe was interlaced, so the first line appeared at the top, the next line (which occupied the next IIRC 40 contiguous bytes of memory) would appear 1/3 of the way down the screen, then the third line would be 2/3 of the way down the screen, then the 4th line would appear under the first. This made graphics really fun :)

At least now i can teach my kids to program the way i learnt. Break out the CALL -151, 6502 machine code rocks!
August 24, 2009 12:01
Nice work. Many thanks for these codes :)

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.