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 \n", 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 \r\n", 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/)提供的大料資料支援,以及期間幫助過墨雲的所有人。