小白自制Linux開發板 十. NES遊戲玩起來

本篇基於我們製作的Debian文件系統而展開,而且我們這會玩一些高級的操作方式——用我們的小電腦進行程序編譯。
 
所以本篇操作全部都在我們個的開發板上完成。
 

1. 開發環境搭建

首先安裝gcc,通常情況下,當製作完成Debian文件系統的時候就已經默認集成了,如果沒有這是用apt-get 進行安裝,然後在命令中輸入:
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-utilsalsa-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(簡易直控媒體層)的縮寫。它是一個跨平臺的多媒體庫,以用於直接控制底層的多媒體硬件的接口。主要用在開發遊戲上!

安裝sdl軟件
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
安裝完成之後,在目錄/usr/include中,就會有個SDL文件夾,裏面包含了所需要的頭文件。

 

2. NES項目配置

上面下載nes代碼,作者已經完成了下面三個部分的修改,所以直接進入arm-NES-linux-master/linux 目錄下執行 make 命令就能直接編譯了。

2.1 修改Makefile

修改arm-NES-linux-master/linux/Makefile 文件
vi Makefile

這裏我們只需要修改最開始的cc=gcc 就行

 終於不用配置交叉編譯工具了,畢竟是小電腦啊 ^_^

2.2 增加鍵盤輸入

InfoNes默認只支持具有專有驅動的遊戲手柄和一種USB通用手柄,但是作者手頭只有鍵盤,所以我們需要添加鍵盤相關的驅動代碼。事實上你下載的代碼已經添加了鍵盤功能,如果想要看看我修改了啥,可以看下面。

這裏需要修改linux/joypad_input.cpp文件,該文件就是用來配置執行遊戲操作相關的代碼。
添加引用、宏定義、以及全局變量
#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/)提供的大料資料支持,以及期間幫助過墨雲的所有人。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章