掌叔
2009-02-14 09:15:19
摘自:[url]http://blog.sina.com.cn/niubods[/url]
作者:niubo
本来想简单的写一下关于如何使用Libnds进行文字输出,图像,按键和触摸屏以及libfat文件系统,不过由于使用了最新版本的devkitpro而改变了主意,这篇基础知识是我断断续续一年来对nd开发的一些理解,其中有些问题甚至困扰了我很长时间。
和其他教程中一样,这一篇不是必看的,里面包括一些你可能目前还不想知道或者不关心的东西,这部分内容可以跳过。不过建议还是全部浏览一遍,就算不理解,有个大体印象就行。
1.“机台程序”
console application一般译作控制台程序。但和电脑上那种运行在DOS窗口下只有文字显示的“控制台程序”有所区别,这个console在这里的意思和在“video game console”和“Handheld game console”中的意思相同。两者的意思分别是“电视游戏机”和“掌上游戏机”,不用说DS就是掌上游戏机,那么DS上的程序就是“游戏机程序”,这里的console应该译作“机台”比较合适。所以我们这里涉及的其实是“机台程序”。
2. 机台程序的特点
相信很多读者都学过C语言。在C语言的介绍中看到C语言功能很强大,可是真正学完了才发现C语言除了能编个“控制台程序”之外什么也干不了。想做Windows程序的话得用VC++,得,还得去学C++……
其实在嵌入式开发领域C语言可是大显神通。关于嵌入式开发我不是很熟悉,我的理解就是嵌入式开发就是做“机台程序”。相对于电脑程序来说这种程序是直接运行与具体硬件的,没有操作系统的支持。所以机台程序和和电脑程序有很大不同。
拿DS为例(因为其他平台没接触过,汗~),用devkitpro+libnds写程序我印象最深的就是——好多宏啊。打开相应的头文件发现大把大把的都是宏定义。
举个例子:#define VRAM_A ((u16*)0x6800000)
这是把后面那个16进制数强制转换成u16类型的指针。u16是16位无符号整形变量,在其他地方定义的,现在不用管,那个数字是一个具体的地址。这么定义之后VRAM_A就可以拿来当作指针来用了,比如可以这么写:VRAM_A[256]。用下标来对以这个地址开头的内存进行操作了。
0x68000000是显存中A块的首地址,也是整个显存的首地址。在帧缓冲模式下可以用这个宏来直接写显存,也就是相当于直接在屏幕上画点。
另外一种定义方式:#define VRAM_A_CR (*(vu8*)0x04000240)
其他都相似就是前面多了个“*”。先举个变量的例子说明下这个星号的作用。
int a = 0;
int *pt = &a;
*pt = 5;
print(“a = %d”,a);
这段代码定义了一个整型变量a和一个指向该整型变量指针pt,用“*pt = 5”进行赋值然后变量a的值就成了5。对这个用法熟悉的可以无视了,但是我在这里可是困扰了好久……其实*pt和pt[0]是等价的,当然*(pt+n)也就是pt[n]。这点其实在指针与数组部分应该有的——啊,扯远了,这里定义这么一个宏,也就可以用 VRAM_A_CR = n来向一个具体地址赋值。用宏定义的好处就是,要操作一个具体地址而要用到指针的特性,不使用变量又免去了额外的内存开支。
VRAM_A_CR是显存A块的控制寄存器。
所以编写机台程序,很多时候是读写某些特定的内存区域或者是修改某些控制寄存器来实现某些功能……
3. nds文件
用devkitpro编译出的可以在DS上运行的文件和官方游戏一样是以nds为后缀的文件,devkitpro是如何把我们的源代码一步步变成nds文件的?
从Windows时代接触编程的人很多都被IDE给宠坏了,不知道源代码是怎么编译成可执行文件的。反正我刚学C语言的时候用的是TC2.0,输入代码然后一个按键就连编译带运行一步搞定。如果对电脑程序的编译过程有所了解的话会发现其实nds程序的编译过程很类似。
我们用devkitpro来编写nds程序用的是其中devkitARM,这套编译工具是针对使用ARM CPU的机器的,可以编译GBA,DS和GP32程序。
首先,源代码要被编译成目标文件,在Windows下后缀是.obj,这里我们用的devkitpro是GNU软件,目标文件后缀是.o。 devkitpro支持C和C++两种语言,它会根据源代码的后缀是.c还是.cpp自行选择编译器。C语言就用gcc,C++就用g++……因为要编译成运行于ARM CPU的程序,所以用的是它们的ARM版本,编译器的名字前面带有arm-eabi-这个前缀。
目标文件只是把源代码编译成机器码,因为没有确定变量的地址也没有库函数的实现部分,并不能直接运行,要通过链接器把目标文件和库文件链接起来并分配好变量所使用的内存。链接后的文件后缀是.elf。链接这一步也是通过相应的编译器来完成的,只是使用了不同的编译参数。
接下来用objcopy这个工具去掉elf文件中的一些无用信息(一些调试信息),生成可以执行的二进制文件。因为DS有两个CPU ARM9和ARM7,运行于两个CPU的源文件要分别编译,这一步生成的二进制文件后缀分别是.arm9和.arm7。
最后,用ndstools把arm9和arm7打包成一个nds文件,并加入图标,标题这样的头部信息。这样生成的nds文件就可以在模拟器或者放进烧录卡在DS上运行了。
4. Makefile
对于这么复杂的编译过程,devkitpro通过一个Makefile文件来解决。Makefile主要包括两部分内容,变量和依赖性规则。关于 Makefile的知识可以在网上搜索一下“Makefile详解”这篇文章,如果能耐心读下来会对看懂示例程序中的Makefile文件很有帮助。通过事先编写好这个类似天书的Makefile文件,我们在devkitpro自带的IDE——Programmers Notepade中执行一个菜单命令Make就可以把一切搞定。
在不同的示例程序中Makefile的内容稍微有些不同,比如有些用到图片的程序,会在Makefile添加一些内容使在编译时调用grit这个程序把 data文件夹下的图片编译成.o和.h文件放在build文件夹下以便把图片转换格式并打包进nds文件内。尽管我们可以把相应示例程序中的 Makefile拷贝到我们自己的工程中使用,但是具备一点Makefile的知识还是很有必要的。
Makefile文件中还依次包含了devkitARM文件夹下的ds_rules,ds_rules中又包含同目录下的base_rules。这两个文件里分别有ds程序的一些规则和基本规则……我比较感兴趣的是在ds_rules中有GAME_TITLE、GAME_SUBTITLE1、 GAME_SUBTITLE2和GAME_ICON这几个变量,可以通过修改这部分内容可以改变编译出的nds文件的标题、副标题和图标。这是新版加入的东西,使用devkitpro编译出的nds文件终于也有图标了。
5. libnds
libnd是一个用来调用DS硬件特性的一个通用库。基本上是一个必需的库。虽然理论上说不用它也是可以的,除非你是一个自虐狂……这个库默认包含在 devkitpro中。我看了一些示例程序,基本上很少用C标准库函数,那些函数对编写DS程序来说功能太有限了。libnds中定义了大量代表内存地址和控制寄存器的宏,以及通过操作相应控制寄存器来实现某些功能的函数。
用老版的libnds,程序中还是有大量直接修改寄存器的语句,让初学者(比如我)看了很头大。新版的libnds 1.3.1中新添了很多函数,很多以前要修改寄存器的语句现在通过调用函数就可以了,源代码清爽了很多。
顺便也提一下其他比较重要的库。
其中之一就是PAlib,这是一个基于libnds的高级库。隐藏了很多底层硬件特性,可以让开发者专注于高级应用,这是一把双刃剑,有好处也有坏处。刚接触DS开发的初学者可能因为看不懂devkitpro附带的nds示例程序而立刻被吓倒(比如我)而相对的PAlib包含有详细的文档资料,网上还有一个详细到几近啰嗦的教程。相信在中国很多爱好者是通过Palib开始接触DS开发的吧。
另一个就是libfat。这个库可以让我们实现C标准库的部分文件操作函数,比如fopen,fread之类。与之相关的另一个关键词是DLDI,libfat内置了一些已知烧录卡的驱动,对于没有内置驱动的烧录卡可以通过打DLDI让自制软件可以运行在自己的烧录卡上。
6. 一点建议
我不知道有多少人像我一样,学过一点C语言就初生牛犊不怕虎地以为可以挑战DS开发了。在中国05、06年的时候应该有过一段时间开发热,在ndsbbs 上有很多技术贴,在我07年终于有了一台DS的时候开发热潮已经趋于冷却了,网上中文资料很少,很多已经过时了,教程附带的例程不知道适用于以前那个版本的devkitpro,没什么比兴冲冲找到一篇教程,却连例程都编译失败更打击人了。
结合我的经验,我建议如果真是从零开始学DS编程的话先安装好开发环境,然后去编译那个“Hello World”例程,看到模拟器成功运行自己编译出的DS程序,稍微能带来些自信。然后可以去看看代码,不明白没关系,至少有个大体印象。再来稍微看看工程文件夹下都是些什么文件夹和文件。
再来就是找些入门级的教程来看看了。这里推荐几个地方:
猴子精灵的博客:[url]http://blog.monkeypotion.net/gameprog/note/nds-dev-part-one[/url]
一个台湾DS开发爱好者写的教程,虽然只有四篇,但是作为入门用还是很不错的,附带的例程用devkitARM r21+libnds-20071023(直接用devkitpro updater1.4.4安装的开发环境)能成功编译。
nash_su’s Blog:[url]http://www.nashsu.com/index.php?cat=8[/url]
介绍了一些比较专业的东西,就是比较乱,看着不大方便。
dev-scene:[url]http://www.dev-scene.com/NDS/Developers[/url]
这个网站太棒了,我的很多困惑都是在这里找到答案,强烈建议到这里看看。虽然是英文的,但是有点英语基础的看懂应该就不能问题。不过教程也是没有完成,估计是烂尾了……
Patater: [url]http://patater.com/manual[/url]
这是一个入门手册……英文的,里面有详细到啰嗦的内容。不过谢天谢地,里面的例程更新至使用libnds1.3.1。
手头有一套libnds的文档资料就更好了,可以深入了解libnds的宏定义和函数的用法。网上有一个,估计是libnds-20071023的,地址是:[url]http://libnds.devkitpro.org/index.html[/url] ,至于新的libnds1.3.1,直接看里面的头文件就可以,本身就相当于文档了,不过这样肯定不是很方便,我用doxygen这个软件根据这些头文件自动生成了一个文档。我把它上传到我的共享资料里了:[url]http://iask.sina.com.cn/user/my_ishare.php?uid=1355689062[/url]
再然后如果对硬件还有点兴趣的话就找点硬件资料来看吧。其实dev-scene上就有很多,觉得不过瘾的话还有这里:[url]http://nocash.emubase.de/gbatek.htm[/url]
有了这些资料应该已经足够,剩下的就是自由发挥了,呵呵。
1989lzhh
2009-02-18 13:51:20
上面这位哥写的文章读起来很亲切啊,很多东西都是我花了昨天一下午才领会的东西,应该早看看啊,就不用走很多弯路了。
白枫
2009-02-26 23:18:08
好东西必须顶,先顶后看
e258691823
2009-03-22 18:48:54
看上去好像不错 帮顶啊
nitro
2009-03-29 19:35:20
你知道哪里交流任天堂的sdk的吗?