每周源代码44-Virtu,C#中针对Silverlight,WPF和XNA的Apple Emulator

[原文发表地址]The Weekly Source Code 44 - Virtu, an Apple Emulator in C# for Silverlight, WPF and XNA

[原文发表时间] 2011-07-14 6:02 PM

我真的倡导人们尽可能多读一些资源,因为读得像写的一样多,Virtu.RasterBlaster

你会成为更好的作家。这是每周源代码的初衷-读代码以便成为更好的开发者。

读开源项目上的代码是个不错的学习方式,特别是如果项目已经有段时间了并且很成功,或者你钦佩致力于此的开发团队 。甚至,通过搜索或者共享代码,你可以找到些代码片断。

我喜欢Emulators。它们很神奇。今年早些时候,当Pete Brown在Silverlight创建了一个C64 Emulator的时候我访问过他

现在,是Apple IIe的时代了。Virtu Project Site,你可以看到这个资源已经以各种形式存在好多年了……形式不断变化。

以这种形式,借助Nick Westgate 的帮助,Sean Fausett 编写了Virtu。该代码很有意思主要有以下原因。首先,因为它是以我喜欢读的语言(注意 不是 C哦 )写成的古怪的AppleIIe emulator,并且结构清晰,包含Silverlight (那意味着MAC同样适用!),WPF XNA (Xbox360)版本。它阐释了把代码分解到引擎和不同宿主的方法。

重要提示: 要运行, Virtue需要两个未包含的文件一份标准或者最好是改善的Apple IIe monitor ROM镜像文件需要被拷贝到Roms目录,命名为 'AppleIIe.rom' (16 KB) 。一份Disk II (16 ) 界面卡片 ROM的镜像需要被拷贝到Roms 目录,命名为 'DiskII.rom' (256 字节 ) 。你还需要一些 ".nib" 文件形式的磁盘,比如RasterBlaster.nib 。我不能给你这些文件。

成功的构建之后,你应该可以通过按神奇的组合键Control+OpenApple+CloseApple+Reset来运行模拟器,执行自我检测。

看一下WpfKeyboardService.cs,我可以看到我没有的那些键怎样影射到我有的那些。

    1: ModifierKeys modifiers = keyboard.Modifiers;
    2: IsOpenAppleKeyDown = keyboard.IsKeyDown(Key.LeftAlt);
    3: IsCloseAppleKeyDown = keyboard.IsKeyDown(Key.RightAlt);
    4: IsResetKeyDown = ((modifiers & ModifierKeys.Control) != 0) 
    5: && keyboard.IsKeyDown(Key.F12);
    6: IsCpuThrottleKeyDown = keyboard.IsKeyDown(Key.F8);
    7: IsVideoFullScreenKeyDown = keyboard.IsKeyDown(Key.F11);
    8: IsVideoMonochromeKeyDown = 
    9: keyboard.IsKeyDown(Key.F9);

看起来是ALT, ALT, CTRL, F12给我一系列奇怪的自我检测的屏幕然后"System OK",这是个好的迹象。

image

不错,现在我可以做一个小的Applesoft BASIC ,摁Ctrl-F12到监视器,输入这个,然后运行。

    1: 10 TEXT:HOME
    2: 20 ?"HELLO WORLD"

激动!

image

虽然时不时的有预料之中的大量的Switch语句,但是代码读起来很有趣,比你想象的模拟器要清晰很多。其它部分肯定感觉是来自以前的,但是,你还能怎么做呢?(不要看VideoData.cs,它会令你很沮丧你脸会融化掉的)例如,这是他们怎么画文本(记住在这我们不用字体,我们已经有个真正低分辨率的屏幕):

    1: private void DrawText40(int 
    2: data, int x, int y)
    3: {
    4: int color = 
    5: Machine.Settings.Video.IsMonochrome ? ColorMono00 : ColorWhite00;
    6: int index = _charSet[data] * 
    7: CharBitmapBytes;
    8: int inverseMask = (_isTextInversed 
    9: && !_memory.IsCharSetAlternate && (0x40 <= data) && 
   10: (data <= 0x7F)) ? 0x7F : 0x00;
   11: for (int i = 0; i 
   12: < TextHeight; i++, y++)
   13: {
   14: data = CharBitmap[index 
   15: + i] ^ inverseMask;
   16: SetPixel(x + 0, y, 
   17: color | (data & 0x01));
   18: SetPixel(x + 1, y, 
   19: color | (data & 0x01));
   20: SetPixel(x + 2, y, 
   21: color | (data & 0x02));
   22: SetPixel(x + 3, y, 
   23: color | (data & 0x02));
   24: SetPixel(x + 4, y, 
   25: color | (data & 0x04));
   26: SetPixel(x + 5, y, 
   27: color | (data & 0x04));
   28: SetPixel(x + 6, y, 
   29: color | (data & 0x08));
   30: SetPixel(x + 7, y, 
   31: color | (data & 0x08));
   32: SetPixel(x + 8, y, 
   33: color | (data & 0x10));
   34: SetPixel(x + 9, y, 
   35: color | (data & 0x10));
   36: SetPixel(x + 10, y, 
   37: color | (data & 0x20));
   38: SetPixel(x + 11, y, 
   39: color | (data & 0x20));
   40: SetPixel(x + 12, y, 
   41: color | (data & 0x40));
   42: SetPixel(x + 13, y, 
   43: color | (data & 0x40));
   44: }
   45: }

在Silverlight,中,他们的技术与Pete Brown'的 C64模拟器曾使用过的(唯一的技术)一样,新的WriteableBitmap类。这意味着XAML只是一个单一图像,每个东西都是动态生成的位图。这是SilverlightVideoService.cs:

    1: namespace Jellyfish.Virtu.Services
    2: {
    3: public sealed 
    4: class SilverlightVideoService : VideoService
    5: {
    6: public 
    7: SilverlightVideoService(Image image)
    8: {
    9: _image 
   10: = image;
   11: SetImageSize();
   12: _bitmap 
   13: = new WriteableBitmap(BitmapWidth, BitmapHeight, 
   14: BitmapPixelFormat);
   15: _pixels 
   16: = new uint[BitmapWidth * BitmapHeight];
   17: Application.Current.Host.Content.Resized 
   18: += (sender, e) => SetImageSize();
   19: }
   20: [SuppressMessage("Microsoft.Usage", 
   21: "CA2233:OperationsShouldNotOverflow", MessageId = "y*560")]
   22: public 
   23: override void SetPixel(int x, 
   24: int y, uint color)
   25: {
   26: _pixels[y 
   27: * BitmapWidth + x] = color;
   28: _pixelsDirty 
   29: = true;
   30: }
   31: public 
   32: override void Update()
   33: {
   34: if 
   35: (Application.Current.RunningOffline && /*_window.IsActive 
   36: &&*/ (_isFullScreen != IsFullScreen))
   37: {
   38: _isFullScreen 
   39: = IsFullScreen;
   40: }
   41: if 
   42: (_pixelsDirty)
   43: {
   44: _pixelsDirty 
   45: = false;
   46: _bitmap.Lock();
   47: for 
   48: (int i = 0; i < BitmapWidth * BitmapHeight; i++)
   49: {
   50: _bitmap[i] 
   51: = (int)_pixels[i];
   52: }
   53: _bitmap.Invalidate();
   54: _bitmap.Unlock();
   55: _image.Source 
   56: = _bitmap; // shouldn't have to set source each frame; SL bug?
   57: }
   58: }
   59: private 
   60: void SetImageSize()
   61: {
   62: Content 
   63: content = Application.Current.Host.Content;
   64: int 
   65: uniformScale = Math.Min((int)content.ActualWidth / BitmapWidth, 
   66: (int)content.ActualHeight / BitmapHeight);
   67: _image.Width 
   68: = uniformScale * BitmapWidth;
   69: _image.Height 
   70: = uniformScale * BitmapHeight;
   71: }
   72: private 
   73: const int BitmapWidth = 560;
   74: private 
   75: const int BitmapHeight = 384;
   76: private 
   77: static readonly PixelFormat BitmapPixelFormat = 
   78: PixelFormats.Bgr32;
   79: private 
   80: Image _image;
   81: private 
   82: WriteableBitmap _bitmap;
   83: private 
   84: uint[] _pixels;
   85: private 
   86: bool _pixelsDirty;
   87: private 
   88: bool _isFullScreen;
   89: }
   90: }

这是个很好的代码库,操作一遍很有意思。如果你对模拟感兴趣,不妨探个究竟。

每个平台,WPF, XNASilverlight.都有Wiki页,上面有细节和奇异之事。仍有工作要做,所以你可以去那看看并提供帮助。