APP下载

嵌入式GameBoyAdvance模拟器移植实验

2016-07-04叶立威姜海阳

电脑知识与技术 2016年15期
关键词:源代码模拟器按键

叶立威+姜海阳

摘要:论文将GameBoyAdvance游戏模拟器移植到嵌入式系统中。借助开放源代码的跨平台多媒体开发库SDL,实现对现有基于PC的GameBoyAdvance模拟器源代码进行移植,包括对窗口的设计、按键事件的判断和音频播放等人机交互界面设计,最终实现了GameBoyAdvance游戏在凌动平台上的运行。

关键词: GameBoyAdvance游戏;SDL库;嵌入式系统

中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2016)15-0122-04

Abstract: GameBoyAdvance game simulator is transplanted to the embedded system. With open source cross platform multimedia development library SDL, the simulator source code based on PC is transplanted, including function such as the window design, key events of judgment and audio playback man-machine interface design, eventually the GameBoyAdvance game in the SDL operation is completed.

Key words: GameBoyAdvance; SDL library; embedded system

1 背景

GameBoyAdvance游戏以其独特的魅力吸引了大量的游戏爱好者。相对Game Boy游戏的早期版本,如GameBoy、Game BoyColor游戏,GameBoyAdvance游戏需要耗费庞大的系统资源,随着嵌入式处理器处理能力的迅猛发展,GBA游戏模拟器在嵌入式处理器的移植、运行成为了可能。模拟器的移植工作是本论文中最大的内容之一,移植工作显得非常重要,它决定着这个论文是否能顺利进行下去。但是移植过程中会出现许多不确定因素,因素是多样的,有被移植代码的因素,也有来自SDL本身接口函数处理的因素,因此被移植代码是否最终能够在系统中正常运行将遇到许多障碍。由于windows下的开发工具非常方便,因此我们的思路是先抛开SDL的移植,在去掉windows的API接口后先能够确保代码在windows上编译通过,然后再将代码进行SDL移植,虽然看似多走了一段路,但这样将移植风险降到了最低。

GameBoyAdvance模拟器能够正常在嵌入式系统下运行,其移植过程是一个充满挑战的旅程,我们选择VC++6.0,通过VC++6.0 windows开发工具的调试修改,使得GBA游戏能够在windows下运行,最终能够移植到嵌入式系统下运行。

2 GameBoyAdvance在SDL上的移植

由于GameBoyAdvance模拟器是开源代码,所以在网上可以找到GameBoyAdvance模拟器的部分代码,但这些代码都只能在系统上编译以及对GBA文件进行解码,但是调用windows的AIP函数会出错,所以没有游戏运行窗口、按键响应、音频播放等基本功能,也就是说,一般用网上的开源源代码编译出来的可执行程序不能直接运行GBA游戏。

SDL是一个用C语言编写的免费跨平台多媒体开发库,使用LGPL许可证,可用于游戏、游戏开发工具、模拟器、样本演示、多媒体应用等。[1]为了添加人机交互界面,我们直接将工程中的windows的API函数抛弃,与跨平台的SDL相结合起来,调用SDL中的一些库函数,从而实现对GameBoyAdvance模拟器的移植。

2.1 SDL库函数的添加

先将开源源代码工程中的main.cpp文件更名newMain.cpp,把 GameBoyAdvance整个工程文件和newMain.cpp文件添加到VC++6.0的工程中,当执行编译时会产生许多错误,部分错误如下:

C:\GBV\vb_no_MMX\vb_no_MMX\win32\newMain.cpp(16): error C2062:type 'int' unexpected

C:\GBV\vb_no_MMX\vb_no_MMX\win32\newMain.cpp(17) : error C2146: syntax error : missing ';' before identifier 'CALLBACK'

C:\GBV\vb_no_MMX\vb_no_MMX\win32\newMain.cpp(17) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int

C:\GBV\vb_no_MMX\vb_no_MMX\win32\newMain.cpp(17) : error C2146: syntax error : missing ';' before identifier 'WndProc'

C:\GBV\vb_no_MMX\vb_no_MMX\win32\newMain.cpp(17) : error C2065: 'HWND' : undeclared identifier

C:\GBV\vb_no_MMX\vb_no_MMX\win32\newMain.cpp(17) : error C2065: 'UINT' : undeclared identifier

C:\GBV\vb_no_MMX\vb_no_MMX\win32\newMain.cpp(17) : error C2065: 'WPARAM' : undeclared identifier

C:\GBV\vb_no_MMX\vb_no_MMX\win32\newMain.cpp(17) : error C2065: 'LPARAM' : undeclared identifier

可以看出,编译出错主要是因为源工程是直接调用windows下API函数,但我们在移植初将这些API函数的头文件删除了,部分删除内容如下:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

所以,如果要进行SDL移植,就要加上SDL库和模拟器源代码的头文件,添加内容如下:

#include

#include

#include

#include

#include

#include

#include "SDL.h"

#include "GBA.h"

#include "agbprint.h"

#include "Flash.h"

#include "Port.h"

#include "debugger.h"

#include "RTC.h"

#include "Sound.h"

#include "Text.h"

#include "unzip.h"

#include "Util.h"

#include "gb/GB.h"

#include "gb/gbGlobals.h"

至此,GameBoyAdvance模拟器开源源代码成功用SDL移植成无人机交互界面的模拟器。

3 为GameBoyAdvance模拟器增加人机交互界面

当GameBoyAdvance在Windows上利用SDL实现了无人机交互界面移植成功后,模拟器还只是 “又聋又哑”,还需要添加人机交互界面才是一个完整的模拟器。这个过程包括了3大部分的内容:游戏画面、键盘响应、声音播放。

3.1 窗体设计

通过SDL建立窗口界面,在newMain.cpp的main函数里添加创建窗口语句,就可以建立在windows下的窗口,部分程序如下:

if(cartridgeType == 0) {

srcWidth = 240;

srcHeight = 160;

systemFrameSkip = frameSkip;

} else if (cartridgeType == 1) {

if(gbBorderOn) {

srcWidth = 256;

srcHeight = 224;

gbBorderLineSkip = 256;

gbBorderColumnSkip = 48;

gbBorderRowSkip = 40;

} else {

srcWidth = 160;

srcHeight = 144;

gbBorderLineSkip = 160;

gbBorderColumnSkip = 0;

gbBorderRowSkip = 0;

}

systemFrameSkip = gbFrameSkip;

} else {

srcWidth = 320;

srcHeight = 240;

}

destWidth = (sizeOption+1)*srcWidth;

destHeight = (sizeOption+1)*srcHeight;

surface = SDL_SetVideoMode(destWidth, destHeight, 16,SDL_ANYFORMAT|SDL_HWSURFACE|SDL_DOUBLEBUF | (fullscreen ? SDL_FULLSCREEN : 0));

3.2 加入键盘响应

为了能响应到按键事件,我们在newMain.cpp文件中利用SDL对按键事件响应的判断,自行编写按键事件响应函数,从而可以达到对按键进行响应,程序如下:

while(SDL_PollEvent(&event) {

if(event.type == SDL_KEYDOWN) {

switch(event.key.keysym.sym) {

case SDLK_UP: // GBA_UP

ZSC_Key_UP();

break;

case SDLK_DOWN : //GBA_DOWN

ZSC_Key_DOWN();

break;

case SDLK_LEFT: //GBA_LEFT

ZSC_Key_LEFT();

break;

case SDLK_RIGHT: //GBA_RIGJT

ZSC_Key_RIGHT();

break;

case SDLK_Enter: //GBA_Enter

ZSC_Key_ Enter ();

break;

case SDLK_A: //GBA_A

ZSC_Key_ A ();

break;

case SDLK_Z: //GBA_Z

ZSC_Key_ Z ();

break;

case SDLK_X: //GBA_X

ZSC_Key_ X ();

break;

case SDLK_Esc:

ZSC_GameExit();

break;

………

}

}

}

3.3 加入声音播放引擎

到目前为止,我们还剩最后的声音处理了,怎样处理声音这一块呢?借助SDL库,将解码声音播放出来,我们最终解决了这个问题。在原本newMain.cpp中被注销掉的代码位置中填入SDL声音引擎。

首先在ZSC_systemSoundInit填入内容下,其中是SDL库对声音驱动的初始化代码如下:

bool ZSC_systemSoundInit()

{

SDL_AudioSpec audio;

switch(soundQuality) {

case 1:

audio.freq = 44100;

soundBufferLen = 1470*2;

break;

case 2:

audio.freq = 22050;

soundBufferLen = 736*2;

break;

case 4:

audio.freq = 11025;

soundBufferLen = 368*2;

break;

}

audio.format=AUDIO_S16SYS;

audio.channels = 2;

audio.samples = 1024;

audio.callback = ZSC_soundCallback;

audio.userdata = NULL;

if(SDL_OpenAudio(&audio, NULL)) {

fprintf(stderr,"Failed to open audio: %s\n", SDL_GetError());

return false;

}

soundBufferTotalLen = soundBufferLen*10;

cond = SDL_CreateCond();

mutex = SDL_CreateMutex();

sdlSoundLen = 0;

systemSoundOn = true;

return true; }

在SDL_AudioSpec audio结构中,定义了声音播放的回调函数ZSC_soundCallback,该函数将游戏中解码的音频数据sdlBuffer不断拷贝到播放数据流中。部分程序如下:

void ZSC_soundCallback(void *,u8 *stream,int len)

{

if(!emulating)

return;

SDL_mutexP(mutex);

if(!speedup && !throttle) {

while(sdlSoundLen < 2048*2) {

if(emulating)

SDL_CondWait(cond, mutex);

else

break;

}

}

if(emulating) {

memcpy(stream, sdlBuffer, len);

}

sdlSoundLen = 0;

if(mutex)

SDL_mutexV(mutex);

}

经过分析,所播放的音频数据是通过WriteSample函数来实现解码程序和播放程序相互连接的,由 WriteSample函数代码和WriteSample在sound.c中被调用可以看出来WriteSample函数是声音处理的关键函数,部分程序如下:

void WriteSample(short int l, short int r)

{

if (audio_is_open == 0) {

return;

}

sample[0][sample_pos] = (short) (l >> 8);

sample[1][sample_pos] = (short) (r >> 8);

sample_pos++;

if (sample_pos >= AUDIOBUFFER) {

play_sample(sample[0], sample[1]);

sample_pos = 0;

}

}

WriteSample中,当音频数据采集到AUDIOBUFFER的长度后 ,音频数据通过 play_sample播放出去。我们将play_sample填入代码,将采集到的音频数据存储在final_wave中,由回调函数waveout播放出去。部分程序如下所示:

void play_sample(signed char *dataL, signed char *dataR)

{

int i;

for (i = 0; i < 2049; i++)

{

sdlBuffer [waveptr] = (dataL[i] +dataR[i] );

waveptr++;

if ( waveptr == 2048 )

{

waveptr = 0;

wavflag = 2; wavdone=0;

}

else if ( waveptr == 1024 )

{

wavflag = 1; wavdone=0;

}

while (!wavdone) SDL_Delay(0);

}

}

因为原代码中的close_audio用来关闭声音设备,而SDL的 SDL_CloseAudio()函数可以用来实现对声音设备的关闭,因此在close_audio中填入代码:

void close_audio(void)

{

SDL_CloseAudio();

audio_is_open = 0;

}

至此模拟器能够显示游戏画面、播放游戏音乐、响应用户指令,已经能够运行在凌动平台下了,有了上述基础下一步只需要做一些小小改动即可运行在嵌入式系统,如linux系统、WinCE等等。移植GameBoyAdvance模拟器运行游戏如图1所示:

4 结束语

论文成功的将GameBoyAdvance游戏模拟器成功移植到凌动平台上。通过测试,支持大部分网络下载的GBA文件格式的游戏。游戏画面清晰流畅,声音,按键操作可靠。论文虽然达到了预期目的,但仍有许多可以改善的地方,如在游戏运行中存在声音有轻微不流畅,画面有轻微卡、顿的现象,将在后续的研究过程中进行改进。

参考文献:

[1] 王园园, 高明煜, 曾毓. 基于 SDL 库的嵌入式平台中文显示技术研究[J]. 计算机系统应用, 2009(4).

[2] 顾叶锋,付宇卓,鲁欣. 基于ARM Linux 的GameBoy模拟器移植和优化研究[J]. 计算机仿真, 2005(9).

[3] 赵成. 嵌入式系统应用基础:基于S3C2410A的SKYEYE的仿真与实践[M]. 北京: 国防工业出版社, 2012.

[4] 谢蓉, 巢爱棠. Linux基础及应用[M]. 北京: 中国铁道出版社, 2005.

猜你喜欢

源代码模拟器按键
人工智能下复杂软件源代码缺陷精准校正
了不起的安检模拟器
基于有限状态机的按键检测程序设计
基于TXL的源代码插桩技术研究
划船模拟器
软件源代码非公知性司法鉴定方法探析
一种多方向导光按键结构设计
揭秘龙湖产品“源代码”
动态飞行模拟器及其发展概述
按键的多种状态检测及消抖处理方法