MatNES-基于Matlab的NES模拟器-PPURAM篇-2

上一篇文章中,已经成功的将PPURAM数据进行了分块,提取出了Pattern Table,Name Table,Attribute Table,Palette等数据,在本篇文章中,主要进行Palette的数据处理,并将其显示出来。

Palette,也就是调色板,在PPU中占有举足轻重的地位,屏幕上显示的所有颜色都是从调色板中提取出来的。由于PPURAM大小的限制,屏幕图像的像素点并不是以RGB数据的形式存储的,而是以调色板索引的形式存储,也就是说,在显示像素的时候,先要从调色板中以查找表的方式将颜色提取出来,然后才能进行显示。(这也是对低下的硬件性能的一种妥协,假若有当今的强大硬件,就不会有这么多的麻烦事了。)

NES只能同屏显示32色,其中还有很多种颜色是重复的(-_-b,太弱了!)。在Palette数据中,每个Byte代表一种颜色,至于具体是什么颜色,请参照下图的颜色表。

根据对应的颜色表,将Palette里的数据转换为RGB颜色,Matlab代码如下:

function RGB    = Byte2Color(Byte)
	switch(Byte)
    	case('00')
        	RGB = '757575';
        case('01')
	        RGB = '271B8F';
    case('02')
    	    RGB = '0000AB';
        case('03')
	        RGB = '47009F';
    	case('04')
        	RGB = '8F0077';
    	case('05')
        	RGB = 'AB0013';
        case('06')
	        RGB = 'A70000';
    	case('07')
        	RGB = '7F0B00';
        case('08')
	        RGB = '432F00';
    	case('09')
        	RGB = '004700';
        case('0A')
	        RGB = '005100';
    	case('0B')
        	RGB = '003F17';
        case('0C')
	        RGB = '1B3F5F';
    	case('0D')
        	RGB = '000000';
        case('0E')
	        RGB = '000000';
    	case('0F')
        	RGB = '000000';
        case('10')
	        RGB = 'BCBCBC';
    	case('11')
        	RGB = '0073EF';
        case('12')
	        RGB = '233BEF';
    	case('13')
        	RGB = '8300F3';
        case('14')
	        RGB = 'BF00BF';
    	case('15')
        	RGB = 'E7005B';
        case('16')
	        RGB = 'DB2B00';
    	case('17')
        	RGB = 'CB4F0F';
        case('18')
	        RGB = '8B7300';
    	case('19')
        	RGB = '009700';
        case('1A')
	        RGB = '00AB00';
    	case('1B')
        	RGB = '00933B';
        case('1C')
	        RGB = '00838B';
    	case('1D')
        	RGB = '000000';
        case('1E')
	        RGB = '000000';
    	case('1F')
        	RGB = '000000';
        case('20')
	        RGB = 'FFFFFF';
    	case('21')
        	RGB = '3FBFFF';
        case('22')
	        RGB = '5F97FF';
    	case('23')
        	RGB = 'A78BFD';
        case('24')
	        RGB = 'F77BFF';
    	case('25')
        	RGB = 'FF77B7';
        case('26')
	        RGB = 'FF7763';
    	case('27')
        	RGB = 'FF9B3B';
        case('28')
	        RGB = 'F3BF3F';
    	case('29')
        	RGB = '83D313';
        case('2A')
	        RGB = '4FDF4B';
    	case('2B')
        	RGB = '58F898';
        case('2C')
	        RGB = '00EBDB';
    	case('2D')
        	RGB = '000000';
        case('2E')
	        RGB = '000000';
    	case('2F')
        	RGB = '000000';
        case('30')
	        RGB = 'FFFFFF';
    	case('31')
        	RGB = 'ABE7FF';
        case('32')
	        RGB = 'C7D7FF';
    	case('33')
        	RGB = 'D7CBFF';
        case('34')
	        RGB = 'FFC7FF';
    	case('35')
        	RGB = 'FFC7DB';
        case('36')
	        RGB = 'FFBFB3';
    	case('37')
        	RGB = 'FFDBAB';
        case('38')
	        RGB = 'FFE7A3';
    	case('39')
        	RGB = 'E3FFA3';
        case('3A')
	        RGB = 'ABF3BF';
    	case('3B')
        	RGB = 'B3FFCF';
        case('3C')
	        RGB = '9FFFF3';
    	case('3D')
        	RGB = '000000';
        case('3E')
	        RGB = '000000';
    	case('3F')
        	RGB = '000000';
        otherwise
	        disp('Invalid HEX');
    end
end

转换完毕后,就可以将整个Palette显示出来了,32种颜色的显示效果如下:

Matlab代码如下:

PaletteDisplay      	   = zeros(2,16,3);
for i               	   = 1:size(ImagePalette,1)
    ImageRGB               = Byte2Color(ImagePalette(i,:));
    SpriteRGB              = Byte2Color(SpritePalette(i,:));
    PaletteDisplay(1,i,1)  = hex2dec(ImageRGB(1:2));
    PaletteDisplay(1,i,2)  = hex2dec(ImageRGB(3:4));
    PaletteDisplay(1,i,3)  = hex2dec(ImageRGB(5:6));
    PaletteDisplay(2,i,1)  = hex2dec(SpriteRGB(1:2));
    PaletteDisplay(2,i,2)  = hex2dec(SpriteRGB(3:4));
    PaletteDisplay(2,i,3)  = hex2dec(SpriteRGB(5:6));
end
image(PaletteDisplay/255);

本文的目标完成,在下一篇文章里,将会进行Pattern Table的显示,敬请期待!

.EOF.转载请注明出处.
EngineeringBay

MatNES-基于Matlab的NES模拟器-PPURAM篇-1

不知道脑子里哪根筋搭错了,忽然对NES模拟器产生了兴趣,这一半是因为同寝室的帽子大神正在用Haskel写这么一个NES模拟器,另一半也许是难以忘却童年时大家一起搓FC的美好时光。于是乎,就想自己实现一个NES模拟器,而且要和别人的完全不同,不单是软件模拟,而且要硬件实现。梁先生有云:“创新启明未来”,世事皆通此理。

我的最终目标是在单片FPGA上实现整个系统,当然这是长远目标,暂时还看不到终点,能在毕业之前完成我就倍感欣慰了。先期目标是使用Matlab软件搭建整个系统(包括CPU、PPU、RAM、卡带、手柄控制器等等)并完成功能验证,之后再使用VHDL编写FPGA代码并进行硬件验证。

NES是Nintendo Entertainment System的缩写,是Nintendo在20世纪80年代和20世纪90年代发售的一种家庭主机,俗称红白机。NES是此类游戏机在欧洲发行版本的缩写,在亚洲发行的游戏机型缩写为FC(Family Computer)又写作Famicom。在该游戏平台上比较著名的游戏有《Contra》,《Super Mario》等等。大家在国内应该很少见到原版的NES,记得小时候玩的最多的还是“小霸王”,“步步高”,“中兴”等品牌的山寨机。虽然外观各异,但内部架构和原版NES是完全一样的。主板上最核心的两块芯片分别是CPU(Center Processing Unit)和PPU(Picture Processing Unit),分别负责核心指令的运行以及图像显示,如下图所示。另外,两块芯片分别附带有2KByte的RAM。

本文作为MatNES系列的第一篇,主要研究的是如何将游戏画面显示在屏幕上。由于屏幕图像数据都是存储在PPURAM里边,通过不断读取PPURAM里的数据并将其显示为一帧一帧的图像,最终形成连续的游戏画面。因此如何将PPU RAM里的数据读取出来,并根据特定的数据结构组合成图像,是本文将要解决的关键问题。

为了简单起见,首先显示一帧的数据。以Super Mario这个游戏为例(这游戏应该没有人会感到陌生吧?),先取出游戏开始后第一帧的PPURAM数据,在这里我是使用Nintendulator这个模拟器将PPURAM数据dump出来的,是一个16进制数据流文件,文件结构如下图所示。

其中的几个Mirrors是逻辑镜像,物理内存中并不存在,因此读取数据的时候要跳过那些部分。PPURAM中主要包括以下几部分的数据:

  • Pattern Table: 模式表,储存屏幕上每个马赛克的形状;
  • Name Table: 名字表,储存屏幕上每个马赛克的位置,这里的位置并非传统意义上的XY位置,而是这个马赛克在模式表中的形状位置,相当于查找表操作;
  • Attribute Table: 属性表,配合名字表使用,描述马赛克的颜色等信息;
  • Palette: 调色板,包括Image Palette和Sprite Palette两部分,分别储存了背景图像的颜色和精灵(自由移动块)的颜色;

对照PPURAM的数据结构,取出每部分的数据,Matlab代码如下:

PatternTable_0      = GPU_RAM_DATA(1:4096,:);
PatternTable_1      = GPU_RAM_DATA(4097:8192,:);
NameTable_0         = GPU_RAM_DATA(8193:9152,:);
AttributeTable_0    = GPU_RAM_DATA(9153:9216,:);
NameTable_1         = GPU_RAM_DATA(9217:10176,:);
AttributeTable_1    = GPU_RAM_DATA(10177:10240,:);
NameTable_2         = GPU_RAM_DATA(10241:11200,:);
AttributeTable_2    = GPU_RAM_DATA(11201:11264,:);
NameTable_3         = GPU_RAM_DATA(11265:12224,:);
AttributeTable_3    = GPU_RAM_DATA(12225:12288,:);
Sprites             = GPU_RAM_DATA(12289:12544,:);
ImagePalette        = GPU_RAM_DATA(12545:12560,:);
SpritePalette       = GPU_RAM_DATA(12561:12576,:);
.EOF.转载请注明出处.
EngineeringBay

使用数学知识来免交违规罚单

人们常说理科知识大都枯燥乏味,理论上很有深度,实际中毫无用处,但也不尽其然。这不,加州大学圣迭戈分校的一哥们就以实际行动告诉我们:将理论与实际相结合,没有什么事情是办不到的!

这哥们的名字叫做Dmitri Krioukov,是加州大学圣迭戈分校的一名物理学家,事情的由来是这样的:当Krioukov开着车在街上悠闲自得地转悠的时候,附近的一位警察发现Krioukov越过了一个停车警示牌,于是警察叔叔毫不犹豫的把他从车上拽下来并开了一张交通违规罚单,罚款美刀400。违反个交通法规就要罚400刀,太坑爹了。要说Krioukov乖乖的交钱认错,息事宁人不就完了吗?不介,他还非要和警察叔叔理论:“不是我违反交通法规,是您眼神儿不太好,没有看准,给我开罚单完全是错误判罚,是有失公允地,是非常遗憾地,是令人难以接受地……”,这还不算完,更NB的是,Krioukov开动了他作为一位物理学家的理论知识天赋,就自己是否违规问题洋洋洒洒的撰写了一篇科技论文,详细分析了自己当时的行车状况,警察的观察角度,路标的位置等信息,最终证明了自己的清白。论文的全文可以在知名的arXiv上找到。

文章的摘要十分风骚:

Abstract: A way to fight your traffic tickets. The paper was awarded a special prize of $400 that the author did not have to pay to the state of California.

文章指出,警察误开罚单原因在于看到Krioukov的时候,警察观测到的是角速度而非线速度,因此误认为Krioukov违反了交通法规,被观测对象离观测者越近,这种情况就越发明显。举例来说,一列线速度恒定的火车,离被观测者距离很远的时候,角速度是非常慢的,当火车驶近观测者的时候,角速度就会变得越来越快,甚至一闪而过。

在当时的情况下,警察在距离停车警示牌100英尺的垂线位置上,当一辆车及恒定线速度驶近警示牌并且刹车的时候,在警察眼中,车的角速度是会急剧上升的,因此导致了警察的错误判断。

法庭的最终审判结果是:Krioukov胜诉,可以免交400美刀的罚单

由此看来,“学好数理化,走遍天下都不怕”这句老话还是有一定道理的。

.EOF.转载请注明出处.

ScienceFacility

巨NB的博士论文致谢

今天看到一段巨NB的博士论文致谢,看内容好像是川大或者成电大神的手笔,此神文言文造诣颇深,行文流畅,而且读起来有王勃《滕王阁序》的味道,其中又夹杂了卧龙先生前后《出师表》的神韵,实在令人佩服。

.EOF.

ScienceFacility

真实世界的Haskell

作者: Bryan O’Sullivan, John Goerzen, Don Stewart

丛书名: 南京东南大学出版社O’Reilly系列

出版社:东南大学出版社

ISBN:9787564119256

上架时间:2010-2-10

出版日期:2010 年2月

开本:16开

版次:1-1

内容简介

《真实世界的Haskell》是一本上手快且易于使用的指导书,它向你介绍这门日趋流行的编程语言。你将学习如何将Haskell应用于不同实践当中,从简短的脚本到要求苛刻的大型应用。本书向你讲解了函数式编程的基础,帮助你加深对如何在现实世界中应用Haskell的理解,例如输入/输出性能、数据处理、并发等等。

《真实世界的Haskell》能帮助你:

* 理解过程式与函数式编程之间的差异 * 学习Haskell的特性,以及如何使用它来开发有用的程序 * 与文件系统、数据库和网络服务交互 * 编写可以进行自动测试、代码覆盖和错误处理的代码 * 通过并发和并行编程发挥多核系统的威力

在本书中你将发现大量的实用习题和真实的Haskell程序示例,你可以修改、编译及运行它们。无论是否曾经使用过函数式语言,如果想要了解 Haskell为何成为众多组织所选用的实用语言,《真实世界的Haskell》是你的首选。

下载地址:

DBANK

.EOF. 转载请注明出处.

EngineeringBay

想读更老的文章吗? →