每周源代码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
你会成为更好的作家。这是每周源代码的初衷-读代码以便成为更好的开发者。
读开源项目上的代码是个不错的学习方式,特别是如果项目已经有段时间了并且很成功,或者你钦佩致力于此的开发团队 。甚至,通过搜索或者共享代码,你可以找到些代码片断。
我喜欢Emulators。它们很神奇。今年早些时候,当Pete Brown在Silverlight创建了一个C64 Emulator的时候我访问过他。
现在,是Apple IIe的时代了。 从Virtu Project Site,你可以看到这个资源已经以各种形式存在好多年了……形式不断变化。
- 最初, 1995年用一些C但主要是ARM汇编语言在Acorn Archimedes上的开发的RISC OS (3.11) 。发布在1997年10月版的Acorn User的cover disk上。后来,我们开始只用带DirectX的C++ 把Virtu移植到 'PC' 机上的Microsoft Windows (95) 。到Microsoft Windows CE (2.11) 的移植接着出现。在接下来的几年里这些有所变化,但从未公布。快进到现在最新的Virtu化身,这次是移植到Microsoft .NET Framework (3.5 SP 1) ,只用带Silverlight , WPF和XNA的C# (Windows 和Xbox 360都有,受限于 .NET Compact Framework)。
以这种形式,借助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",这是个好的迹象。
不错,现在我可以做一个小的Applesoft BASIC ,摁Ctrl-F12到监视器,输入这个,然后运行。
1: 10 TEXT:HOME
2: 20 ?"HELLO WORLD"
激动!
虽然时不时的有预料之中的大量的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, XNA 和Silverlight.都有Wiki页,上面有细节和奇异之事。仍有工作要做,所以你可以去那看看并提供帮助。