每周源代码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页,上面有细节和奇异之事。仍有工作要做,所以你可以去那看看并提供帮助。


Comments (0)

Skip to main content