![知乎周源微信](https://i-blog.csdnimg.cn/blog_migrate/684d70b4bff5a7f389db8032b8d9d05f.png)
知乎周源微信
![C64 Application Icon C64 Application Icon](https://i-blog.csdnimg.cn/blog_migrate/3b417d86687a0342217dd76ea55f033e.png)
I had the pleasure of interviewing Pete Brown this last week and talking about the Silverlight 3 Commodore 64 Emulator he's been working on. He just launched the CodePlex site a few minutes ago (my time), but I've had the code for a while to play with. You can read Tim Heuer's blog post for details on how to get started with Silverlight 3 Beta and the tools you'd need or see some video of the emulator in action.
我很高兴在上周采访了Pete Brown,并谈到了他一直在研究的Silverlight 3 Commodore 64 Emulator 。 他是在几分钟前(我的时间)刚刚启动CodePlex网站的,但是我已经有一段代码可以玩了。 您可以阅读Tim Heuer的博客文章,详细了解如何开始使用Silverlight 3 Beta和所需的工具,或者观看运行中的仿真器的一些视频。
Keep in mind that this is a labor of love that Pete's doing, and the code has been written in "gett'er done" mode, so it won't win any awards for aesthetic. A lot of the code as been ported directly over from Open Source C++ in the Frodo Emulator or from Sharp C64.
请记住,这是皮特(Pete)所做的一项热爱工作,并且该代码已以“完成”的方式编写,因此不会赢得任何美观方面的奖项。 许多代码直接从Frodo Emulator中的Open Source C ++或Sharp C64移植过来。
It does have some pretty clever ideas, though, and I thought I'd take a look at those in this Weekly Source Code (which I promise to make more Weekly, starting now).
不过,它确实有一些非常聪明的想法,我想我会看一下本“每周源代码” (我保证从现在开始,将使更多“每周” )中的那些想法。
动态创建视频流 (Dynamically Creating a Video Stream)
Pete wanted the screen to draw as fast as possible, which is 50Hz (50 times a second). He was originally creating PNGs or Bitmaps and throwing it up on the screen as fast as possible, but then a member of the Silverlight team suggesting "making a video." What did he mean by "making a video?" He suggested actually using a Silverlight MediaElement (the "video player" control) and acting as a DataSource for a video. He's dynamically creating a movie that never ends.
皮特希望屏幕尽可能快地绘制,即50Hz(每秒50次)。 他最初是创建PNG或位图,然后将其尽快投放到屏幕上,但是后来Silverlight小组的成员建议“制作视频”。 他所说的“制作视频”是什么意思? 他建议实际上使用Silverlight MediaElement(“视频播放器”控件)并充当视频的数据源。 他正在动态地创作一部永无止境的电影。
This means the UI XAML is basically:
这意味着UI XAML基本上是:
<MediaElement x:Name="VideoDisplay"
Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Top"
Stretch="Uniform"
IsHitTestVisible="False"
Margin="4" />
And in the code behind he creates a VideoMediaStreamSource the had blogged about here, deriving from MediaStreamSource:
然后,他在后面的代码中创建了一个VideoMediaStreamSource,该视频来自MediaStreamSource,该博客已在此处发布:
_video = new VideoMediaStreamSource(TheSID.Renderer.AudioStream, C64Display.DISPLAY_X, C64Display.DISPLAY_Y);
and it looks like:
它看起来像:
private byte[][] _frames = new byte[2][];
public VideoMediaStreamSource(int frameWidth, int frameHeight)
{
_frameWidth = frameWidth;
_frameHeight = frameHeight;
_framePixelSize = frameWidth * frameHeight;
_frameBufferSize = _framePixelSize * BytesPerPixel;
// PAL is 50 frames per second
_frameTime = (int)TimeSpan.FromSeconds((double)1 / 50).Ticks;
_frames[0] = new byte[_frameBufferSize];
_frames[1] = new byte[_frameBufferSize];
_currentBufferFrame = 0;
_currentReadyFrame = 1;
}
public void Flip()
{
int f = _currentBufferFrame;
_currentBufferFrame = _currentReadyFrame;
_currentReadyFrame = f;
}
When he wants to write a pixel to his buffer, as he often does at the low level:
当他想要一个像素写信给他的缓冲,因为他经常做在较低水平:
public void WritePixel(int position, Color color)
{
int offset = position * BytesPerPixel;
_frames[_currentBufferFrame][offset++] = color.B;
_frames[_currentBufferFrame][offset++] = color.G;
_frames[_currentBufferFrame][offset++] = color.R;
_frames[_currentBufferFrame][offset++] = color.A;
}
When it comes time to get a sample, the MediaSteamSource calls GetSampleAsync:
当需要获取样本时,MediaSteamSource调用GetSampleAsync:
protected override void GetSampleAsync(MediaStreamType mediaStreamType)
{
if (mediaStreamType == MediaStreamType.Audio)
{
GetAudioSample();
}
else if (mediaStreamType == MediaStreamType.Video)
{
GetVideoSample();
}
}
He grabs a video frame from his buffer, he make a sample and reports he's done:
他从缓冲区中抓取一个视频帧,制作了一个样本并报告了他的完成:
private void GetVideoSample()
{
_frameStream = new MemoryStream();
_frameStream.Write(_frames[_currentReadyFrame], 0, _frameBufferSize);
// Send out the next sample
MediaStreamSample msSamp = new MediaStreamSample(
_videoDesc,
_frameStream,
0,
_frameBufferSize,
_currentVideoTimeStamp,
_emptySampleDict);
_currentVideoTimeStamp += _frameTime;
ReportGetSampleCompleted(msSamp);
}
His app makes frames as fast as they can, putting them in the buffer at 50Hz, and the MediaElement requests frames from his VideoMediaStreamSource as fast as it can take them.
他的应用程序以最快的速度制作帧,并将其以50Hz的频率放入缓冲区,并且MediaElement尽可能快地从他的VideoMediaStreamSource请求帧。
模拟1541磁盘驱动器 (Emulating a 1541 Disk Drive)
There's a file format in the world of C64 emulators that everyone has standardized on called .d64. The D64Drive.cs file contains the meat of the code to read these image files. "The *.D64 file format is a 1:1 copy of all sectors as they appear on a floppy disk."
C64仿真器世界中有一种文件格式,每个人都对此格式进行了标准化,称为.d64。 D64Drive.cs文件包含读取这些图像文件的代码段。 “ * .D64文件格式是所有扇区出现在软盘上的1:1副本。 ”
Most of it looks like C/C++ code, because it once was. Some of it used to be "unsafe" C# code, writing with the unsafe keyword so the runtime could pin down pointers and use them directly.
因为它曾经是,所以大多数看起来像C / C ++代码。 其中一些曾经是“不安全的” C#代码,使用unsafe关键字编写,因此运行时可以固定指针并直接使用它们。
I love it when there's things like byte[] magic. ;) Seems like every binary file format has them. In this case, we're looking for 0x43, 0x15, 0x41 and 0x64. Notice that 0x43 is "C", while the second and third bites are "1541" with the final "64" in there. ;)
当有byte []魔术之类的东西时,我会喜欢它。 ;)似乎每种二进制文件格式都有它们。 在这种情况下,我们正在寻找0x43、0x15、0x41和0x64。 请注意,0x43是“ C”,而第二和第三位是“ 1541”,最后是“ 64”。 ;)
private void open_close_d64_file(string d64name, Stream fileStream)
{
long size;
byte[] magic = new byte[4];
// Close old .d64, if open
if (the_file != null)
{
close_all_channels();
the_file.Dispose();
the_file = null;
}
// Open new .d64 file
if (fileStream != null)
{
//the_file = new FileStream(d64name, FileMode.Open, FileAccess.Read);
the_file = fileStream;
// Check length
size = the_file.Length;
// Check length
if (size < NUM_SECTORS * 256)
{
the_file.Dispose();
the_file = null;
return;
}
// x64 image?
the_file.Read(magic, 0, 4);
if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64)
image_header = 64;
else
image_header = 0;
// Preset error info (all sectors no error)
Array.Clear(error_info, 0, error_info.Length);
// Load sector error info from .d64 file, if present
if (image_header == 0 && size == NUM_SECTORS * 257)
{
the_file.Seek(NUM_SECTORS * 256, SeekOrigin.Begin);
the_file.Read(error_info, 0, NUM_SECTORS);
}
}
}
This is all fun stuff, but as Pete said to me in an email:
这些都是好玩的东西,但正如Pete在电子邮件中对我说的那样:
"PS. My “real” code *never* looks like this. This is full of c++-isms and just plain “let me see if I can get this to work” junk."
“ PS。我的“真实”代码*从来没有*看起来像这样。它充满了c ++-isms,只是“让我看看我是否可以使它工作”。
So, take it for what it is. It's awesome.
因此,按原样使用它。 这很棒。
References
参考文献
Frodo Emulator: http://frodo.cebix.net/
Frodo模拟器: http : //frodo.cebix.net/
Sharp C64: http://sourceforge.net/projects/sharp-c64
夏普C64: http : //sourceforge.net/projects/sharp-c64
翻译自: https://www.hanselman.com/blog/the-weekly-source-code-39-commodore-64-emulator-in-silverlight-3
知乎周源微信