GBA程序开发入门
-
1.GBA简介
Game Boy Advanced(GBA)是日本任天堂公司于2001年推出的一款32位掌上游戏机.它着重于游戏机的便携式,并且以2D游戏为主(3D游戏依然不错).GBA有十分高效硬件图像处理加速,GBA基本上是现在全球公认的最佳的掌上游戏机.
GBA的硬件功能十分多.它的CPU是以RISC为基础的32位ARM CPU,主频是16.78MHz.屏幕最大支持240x160的16位真彩色显示.在图形处理方面GBA还另外提供了硬件加速,支持硬件上的图形旋转,缩放,alpha混合,face in/out淡入淡出等.
GBA的软件开发主要是以C语言为主,程序设计简单而且十分自由,也正是这一点吸引了许多爱好者在GBA上做东西(不一定是游戏).你完全可以把它做成PDA,做成Mp3播放器(它的声音处理方面能力也不错哦),做成随身电影播放齐,做成电子词典等,只要你有能力设计它的软件.
比如azhong的主页http://huangyz2002abcd.nease.net/上就有他写的WORDBOY字典软件.还有MUSIC PLAYER GBA的音乐播放器,能让GBA变身Mp3播放器.VideoGBA,能把图片转换成GBA影片的工具.不过要制做它的电影有点困难.这里提供一部已经做来的,<<黑客帝国>>的片断下载: http://213.30.21.181/web/down/show.asp?id=40&down=1
在http://readboy.nease.net/上还可以找到READBOY,GBA上的读书软件,这个软件做得很不错,除了看书,甚至还可以浏览图片.
不说多了,我们还是主要来看看如何开发GBA的软件.应该说,你只要会点C语言,就可以进入GBA的开发行列中了.在GBA上写程序比在电脑上写程序更简单.GBA是单总线的设计,也就是说通过简单的读写命令就能完成所有的内部硬件操作.访问GBA内部的寄存器就犹如访问一块内存一样简单,而GBA所有的功能都是通过读写寄存器而完成的.
2.GBA程序开发准备
GBA开发所需要的东西除了一台可以用的电脑外,主要就是一个编译器和一个GBA模拟器.
有了GBA模拟器,你就可以在你的电脑上运行GBA程序了.GBA模拟器应该是到处可以找得到的.我推荐的是VisualBoyAdvance.你可以在www.gbadev.org上下载最新的版本.如果你还想把你的程序弄到GBA机器上玩,那么除了应该有个GBA外,还应该有个ROM烧录器.比如EZ-Flash.通过它可以把电脑上的ROM烧录到GBA卡带上,然后放到GBA上运行.(总共一个GBA加一个128MB的EZ-Flash大概是1000元人民币)
GBA程序开发可以用汇编语言和C/C++语言来写程序,不过汇编语言比较复杂,而且不是我们平常使用8086汇编,而是Arm汇编.任天堂公司可能觉得C++程序运行消耗太大,不推荐C++语言来开发,所以我们都一直将C语言作为首选.
GBA开发所用的C语言编译器主要有两种,一种是免费的gcc,一种是Arm公司提供的ArmSDT.
这里的gcc当然不是简单的linux下的gcc,而是Arm Gcc.简单地说就是把我们平时常见的gcc改造成生成ARM CPU代码的gcc.你可能会问难道我们要使用linux操作系统了吗?其实我们还是在windows下做GBA开发,虽然gcc主要是在linux下的东西,但是我们也可以从cygwin(在windows下模拟UNIX的工具)中得到在windows下运行的gcc.你可以根据这个cygwin下的gcc,自己去配置Arm Gcc,也可以直接去下载个配置好了的Arm Gcc.比如DevKitAdv,你可以到http://www.gbadev.org 去下载这个软件.DevKitAdv大概10多MB.有了它,你用不着去找什么cygwin(200-300MB)和配置什么Arm Gcc了,DevKitAdv已经帮你做好了一切,你只需安装它就OK了.
ArmSDT是Arm公司提供的专门为ARM CPU的开发程序的C语言编译器.前面说过,GBA是采用32位的ARM CPU,所以这个ArmSDT是可以用来开发GBA程序的.而且由于ArmSDT是ARM公司官方的开发包,所以它的编译速度和质量上都比免费的gcc要高,但对C语言的支持也不及gcc好(gcc毕竟已经成了C语言编译器的标准),最关键的问题它不是免费的,但是它的免费测试版还是可以用的.你可以到Arm公司的网站去下载ARMSDT2.51. http://www.arm.com
我以DevkitAdv配置的gcc为主还讲解GBA的程序开发.
安装DevKitAdv很简单,用WinZip或WinRAR软件解压下载文件后,双击DKA_Setup.exe,出现下面的对话框,选择安装目录,然后点击"Install"就进行安装了.整个DevKitAdv共49MB,应该是十分小的.
编译器和模拟器都装好后,下面我们需要一批小工具,主要就是帮我们转换图片,声音等数据.GBA程序编译后通常都是一个rom文件,图像和声音等数据都是包含在主程序里的.这些工具能把我们电脑上的图片或声音文件转换成一个*.h头文件,然后包含在主程序里编译.关于这种转换工具也是有许多,我推荐的图形转换工具是kaleid_1-2-3,在http://www.gbadev.org上下载,它可以转换我们平时用的BMP文件.声音转换工具是wav2gbac.exe,也是在http://www.gbadev.org下载,它可以转换我们平时用的WAV文件.
好了,下载完后这两个小工具后,准备工作就算做完了.
3.第一个GBA程序
现在我们开始我们的第一个GBA程序.在这个程序中我们将在屏幕上显示一张的图片.
首先你建立一张240*160大小,256色的bmp图片,比如这样:
名字就叫helloworld.bmp.打开刚才下载的Kaleid_1-2-3程序.选中菜单File中Open,打开这个helloworld.bmp文件,再选中菜单File中Convert and Save.出现对话框:
如图一样选择各项选择,特别注意的是一定要选择"BitMap Graphics(Mode 4)"和"Save As C Source",并且去掉"#include <agbtypes.h>的选择.最后单击"Convert and Save",保存为helloworld.h
图像数据已经转换好了,接着我们开始写代码.建立个main.c的文件,并把helloworld.h放在一个目录下.然后在main.c中写下代码
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;
#define REG_DISPCNT *(u16*)0x04000000 // 显示寄存器地址
#define VRAM 0x06000000 // 图像缓冲区地址
#define PALETTE 0x5000000 // 调色板地址
#define MODE_4 0x04 // mode4标志
#define BG2_ENABLE 0x0400 // BG_2标志
#define SetMode(Mode) REG_DISPCNT=(Mode) // 设置显示模式的宏定义
#include "helloworld.h" // 包含图像调色板和数据的头文件
u16* palette_mem=(u16*)PALETTE; // 系统调色板
u16* video_buffer=(u16*)VRAM; // 图像缓冲区
void Draw(u16* src_palette,u16* src_data,u16* dst_palette,u16* dst_data);
int main()
{
// 设置屏幕模式,这里使用MODE_4
SetMode (MODE_4 | BG2_ENABLE);
// helloworld_pal和helloworld_gfx是在"helloworld.h"定义的调色板和图像数据数组名
Draw((u16*)helloworld_pal,(u16*)helloworld_gfx,palette_mem,video_buffer);
// 死循环
while(1)
{ ;}
}
// MODE_4绘图函数
void Draw(u16* src_palette,u16* src_data,u16* dst_palette,u16* dst_data)
{
int loop,x,y;
// 写入目的调色板
for(loop = 0; loop < 256; loop++)
dst_palette[loop] = src_palette[loop];
// 写入图像缓冲区
for(x = 0; x < 120; x++)
for(y = 0; y < 160; y++)
dst_data[(y) *120 + (x)]=src_data[(y) *120 + (x)];
}
最后建立一个编译批处理命令make.bat来帮我们编译生成目标代码.
写入下面的命令到一个新建的make.bat文件里面
PATH=C:\devkitadv\bin;%PATH%
gcc -lm -o main.elf main.c
objcopy -v -O binary main.elf main.bin
pause
如果你的devkitadv是在D盘,那么就应该是PATH=D:\devkitadv\bin;%PATH%,我这里的DevKitAdv是安装在C盘的.执行make.bat,如果没有错误的话就可以得到编译出来的main.bin和main.elf. main.bin就是可以运行的GBA的rom.你可以用的模拟器VisualBoyAdvance来打开运行它,或者烧录到GBA卡带上运行.
这是在模拟器VisualBoyAdvance中运行的结果.
下面我将一一讲解上面的程序
前三行:
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;
定义GBA程序自己的数据类型,因为这样我们就很容易知道每个数据在GBA中占的字节数.
#define REG_DISPCNT *(u16*)0x04000000 // 显示寄存器地址
REG_DISPCNT是设置显示模式的寄存器.GBA中有mode0-mode5六个显示模式.我们这个程序的显示模式是mode4,也就是240x160的8位色(256色)的双缓冲显示模式.比如mode3,就是240x160的16位真彩色的单缓冲显示模式.
#define VRAM 0x06000000 // 图像缓冲区地址
#define PALETTE 0x5000000 // 调色板地址
VRAM就是GBA的显示内存,简单地说VRAM中的数据就是屏幕上的图象.跟我们的平常用的计算机一样的.PALETTE就是调色板地址,简单地说就是装图像调色板的地方.
#define MODE_4 0x04 // mode4标志
#define BG2_ENABLE 0x0400 // BG_2标志
MODE_4和BG2_ENABLE都是设置REG_DISPCNT的值.
我们再看看main函数主要做了些什么.
首先是SetMode (MODE_4 | BG2_ENABLE); 把显示模式设置成mode_4.然后是执行Draw函数,把我们刚刚转换的helloworld.h里面的图像数据和调色板数据复制指定的内存去.在Draw这个函数里面,都是使用了for循环一个一个地把调色板数据和图象数据复制到内存中去的.
前面提到过,GBA中MODE4支持的是240*160并且256色的显示方式,那么显示内存VRAM中一个字节就对应屏幕上的一点的颜色.这么算来,整张240*160大小的屏幕就应该有240*160=38400个字节.所以我们在helloworld.h中的helloworld_gfx有38400/2=19200个元素(一个u16是占两个字节).而且我们还可以通过公式来计算得到屏幕中一点对应在VRAM的地址.这样就可以得到下面的写点函数.特别指出的是,对于VRAM的访问只能是u16大小的数据类型,而不能是u8(包括char).而一个u16类型的数据表示着两个点的信息,低8位对应的是前面的点,高8位对应的是后面的点. 所以我们需要一些算法来实现对屏幕中单独一个点的读写,请看下面的写点函数
void SetPixel(int x,int y,u8 color)
{
register u16 *tc;
tc= video_buffer+y*120+x/2;
if(x&1) // 如果x是奇数
*tc=((*tc&0x00FF)+(color<<8));
else
*tc=(*tc&0xFF00)+color;
}
其中video_buffer就是显示内存VRAM的地址.屏幕上的点与VRAM中的值是按线性一一对应的.VRAM中第1个字节的信息就是屏幕中(0,0)这个点的颜色在调色板上的引索,第2个字节就是屏幕(1,0)的颜色信息,第3个字节就是(2,0)的颜色信息,一次类推.到第241个字节就是屏幕(0,1)的颜色信息.然后第242就是(1,1)的信息...由此关系,我们可以找出公式,在屏幕上(x,y)点的信息应该是储存于VRAM中第 y*240+x 个字节里,那么我们如果要画(x,y)这一点,就只需要修改VRAM中第y*240+x个字节的信息就OK了.可是这里又有个问题困扰着我们,那就是上面黑体字写的,GBA中对于VRAM的访问不能是单单的一个字节的访问,而必须是16位两个字节的访问.这也就是说你每次写点的同时会影响到旁边的点的信息.还好,我们可以通过简单的位运算可以在避开影响旁边点的同时,完成对(x,y)的写点工作.代码已经在上面了.由于*tc是16位的指针,所以tc加1就等于移动了16位两个字节的距离.所以在代码中,tc=video_buffer+ y*120+ x/2 而不是前面所说的tc=video_buffer+ y*240 +x.至于后面的代码我也就不多讲了,如果你觉得有问题,那么应该去查看一下C语言书籍.
通过这个函数,你就可以在屏幕上画线,画圆,画矩型等,甚至还可以写汉字.