1. 开发环境搭建
gcc -v
如果显示如下信息
Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabi/8/lto-wrapper Target: arm-linux-gnueabi Configured with: ../src/configure -v --with-pkgversion='Debian 8.3.0-6' --with-bugurl=file:///usr/share/doc/gcc-8/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-8 --program-prefix=arm-linux-gnueabi- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libitm --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-sjlj-exceptions --with-arch=armv5te --with-float=soft --disable-werror --enable-checking=release --build=arm-linux-gnueabi --host=arm-linux-gnueabi --target=arm-linux-gnueabi Thread model: posix gcc version 8.3.0 (Debian 8.3.0-6)
就表示我们的小板已经安装了编译器了。
接下来我们就开始移植NES游戏机模拟器程序,这里使用InfoNes,代码下载:
https://files.cnblogs.com/files/twzy/arm-NES-linux-master.zip
InfoNES 音频部分需要alsa相关的组件,大概有两个alsa-utils和 alsa-lib,这个组件我们在音频部分有过了解。
对于alsa-utils可以直接通过 apt-get install alsa-utils 进行安装,此处不做过多细说,但是对于alsa-lib作者并没有在找到可以直接安装的包,所以我们手动编译。
1.1 安装alsa-lib
首先下载alsa-lib库
https://files.cnblogs.com/files/twzy/alsa-lib-1.2.5.1.zip
然后把zip文件上传到到我们的开发板上面,解压进入alsa-lib-1.2.5.1目录中,接下来就是Linux三板斧安装方法:
- 执行 ./configure 进行项目配置
- 执行 make 进行编译
- 执行 make install 进行安装
因为alsa-lib项目较大,我们的小电脑很弱,所以你会等很长时间。
安装完成以后,在目录/usr/include中,就会有个alsa文件夹,里面就有项目使用的头文件。
1.2 安装SDL(可选)
本节为可选项,因为在墨云编译其他版本的nes模拟器的时候,有要求需要用到这个库,所以这个开发板已经安装了,根据作者经验应该大概率不用安装,读者可以自行决定。
SDL是Simple DirectMedia Layer(简易直控媒体层)的缩写。它是一个跨平台的多媒体库,以用于直接控制底层的多媒体硬件的接口。主要用在开发游戏上!
apt-get install libsdl1.2-dev
sudo apt-get install libsdl-image1.2-dev sudo apt-get install libsdl-mixer1.2-dev sudo apt-get install libsdl-ttf2.0-dev sudo apt-get install libsdl-gfx1.2-dev
2. NES项目配置
2.1 修改Makefile
vi Makefile
这里我们只需要修改最开始的cc=gcc 就行
终于不用配置交叉编译工具了,毕竟是小电脑啊 ^_^
2.2 增加键盘输入
InfoNes默认只支持具有专有驱动的游戏手柄和一种USB通用手柄,但是作者手头只有键盘,所以我们需要添加键盘相关的驱动代码。事实上你下载的代码已经添加了键盘功能,如果想要看看我修改了啥,可以看下面。
#include <linux/input.h> //此处需要用到 input_event结构 #define KEYBOARD_DEV "/dev/input/event0" //键盘所在的文件、请根据实际情况进行调整 …… static int joypad_fd; static int USBjoypad_fd; static int keyboard_fd; //新增加的用于存储键盘的句柄 static PT_JoypadInput g_ptJoypadInputHead; ……
接下来是添加键盘结构体,以及相关初始化、释放、获取值相关的代码。
这里我们重点看一下KeyBoardGet() ,在nes游戏中我们只需要配置8个键就可以了,对应如下表所示:
代码如下:
//****************************keyBoard**************************************** static int KeyBoardGet(void) { /** * FC手柄 bit 键位对应关系 真实手柄中有一个定时器,处理 连A 连B * 0 1 2 3 4 5 6 7 * A B Select Start Up Down Left Right * * o p <space> <Enter> w s a d * 24 25 57 28 17 31 30 32 * * 来自 /usr/include/linux/input-event-codes.h */ //因为 USB 手柄每次只能读到一位键值 所以要有静态变量保存上一次的值 static unsigned char joypad = 0; struct input_event e; //这里使用标准的input_event结构体 int result = -1; result = read(keyboard_fd, &e, sizeof(struct input_event)); if (result != sizeof(struct input_event)) { printf("key error %d ", result); return -1; } //printf("value:%u type:%u code:%u ", e.value, e.type, e.code); if (0x01 == e.type) //EV_KEY 0x01 { /*上 W */ if (1 == e.value && 17 == e.code) { joypad |= 1 << 4; } if (0 == e.value && 17 == e.code) { joypad &= ~(1 << 4); } /*下 S*/ if (1 == e.value && 31 == e.code) { joypad |= 1 << 5; } if (0 == e.value && 31 == e.code) { joypad &= ~(1 << 5); } /*左 A*/ if (1 == e.value && 30 == e.code) { joypad |= 1 << 6; } if (0 == e.value && 30 == e.code) { joypad &= ~(1 << 6); } /*右 D*/ if (1 == e.value && 32 == e.code) { joypad |= 1 << 7; } if (0 == e.value && 32 == e.code) { joypad &= ~(1 << 7); } /*选择 space*/ if (1 == e.value && 57 == e.code) { joypad |= 1 << 2; } if (0 == e.value && 57 == e.code) { joypad &= ~(1 << 2); } /*开始 enter*/ if (1 == e.value && 28 == e.code) { joypad |= 1 << 3; } if (0 == e.value && 28 == e.code) { joypad &= ~(1 << 3); } /*A O*/ if (1 == e.value && 24 == e.code) { joypad |= 1 << 0; } if (0 == e.value && 24 == e.code) { joypad &= ~(1 << 0); } /*B P*/ if (1 == e.value && 25 == e.code) { joypad |= 1 << 1; } if (0 == e.value && 25 == e.code) { joypad &= ~(1 << 1); } } return joypad; } static int KeyBoardDevInit(void) { keyboard_fd = open(KEYBOARD_DEV, O_RDONLY); if (-1 == keyboard_fd) { printf("%s dev not found ", KEYBOARD_DEV); return -1; } return 0; } static int KeyBoardDevExit(void) { close(keyboard_fd); return 0; } static T_JoypadInput KeyBoardInput = { KeyBoardDevInit, KeyBoardDevExit, KeyBoardGet, }; //********************************************************************
最后我们来注册一下键盘,修改int InitJoypadInput(void) 函数
int InitJoypadInput(void) { int iErr = 0; //iErr = RegisterJoypadInput(&joypadInput); //iErr = RegisterJoypadInput(&usbJoypadInput); iErr = RegisterJoypadInput(&KeyBoardInput); //这里我们只注册键盘 return iErr; }
2.3 修改偏色问题
键盘修改完毕,接下来就改一下屏幕偏色的问题,这里只需要修改 linux/InfoNES_System_Linux.cpp文件中的static int lcd_fb_display_px函数(调整spi屏幕的颜色):
static int lcd_fb_display_px(WORD color, int x, int y) { // unsigned char *pen8; // unsigned short *pen16; // pen8 = (unsigned char *)(fb_mem + y*line_width + x*px_width); // pen16 = (unsigned short *)pen8; // *pen16 = color; // return 0; //修改InfoNES_System_Linux.cpp文件中的static int lcd_fb_display_px函数(调整spi屏幕的颜色): WORD *pen16; unsigned char r, g, b; r = ((color >> 10) & 0x1f); g = ((color >> 5) & 0x3f); b = (color & 0x1f); color = r<<11|g<<6|b; pen16 = (WORD *)(fb_mem + y*line_width + x*px_width); *pen16 = color; return 0; }
接下来就在linux目录下执行
make
命令,等待编译结束就可以在当前目录下看到一个叫InfoNES的软件,这就是编译好的模拟器,接下来执行命令(我的游戏存放在/root/game/目录下)
./InfoNES /root/game/h.nes
不出意外就可以看到如下的画面
所以还不玩起来……
4. 遗留问题
- 画面 ~ 在操作游戏的时候、因为FrameBuffer的关系,可以明显看到光标导致的画面异常;
- 声音 ~ 只能算是听个响,真的;
所以后续随缘解决吧。
随着本篇的结束,本项目也算是正式结束,所以本系列也就到此为止、后续墨云会根据实际情况将这个系列中的各种问题集中汇总处理一下。最后感谢关注过墨云的你,最主要的是感谢哇酷开发者社区(https://whycan.com/)提供的大料资料支持,以及期间帮助过墨云的所有人。