iMX6ULL アプリケーションの移植 | infoNES エミュレータの移植 (古典的な NES ゲームの再プレイ)

NES ゲームをプレイしなかった子供時代は、1980 年代生まれの子供時代ではないかもしれません。僕らは幼い頃、FCからゲーム機に触れ始め、当時大ヒットしていて、中学生の時にお金を貯めてシャオバワンを買いました。テレビを爆破してしまった!当時、任天堂のFCゲーム機の売上だけで米国の全テレビ局の売上を合計した売上を上回り、任天堂というブランドが人々の心に定着しました。

序文

1983 年 7 月 15 日、日本の任天堂株式会社の宮本茂氏によって開発された第 3 世代の家庭用電子ゲーム機 (元々は日本式ポーカーまたは「ハナザ」を開発): FC、正式名称: ファミリー コンピュータ、ファミコンとも呼ばれます。ヨーロッパやアメリカで販売されている場合は、nes と呼ばれ、正式名称は Nintendo Entertainment System です。中国本土、台湾、香港では、一般的に「赤と白」と呼ばれています。赤と白の筐体が特徴的な「マシン」が正式に市場に参入し、後に大成功を収め、家庭用ゲーム機が世界各地で世界的に普及する序曲となりました。

InfNESとは何ですか?

NESゲームエミュレータ。InfoNES はさまざまなプラットフォームに簡単に移植できます。作者は Martin Freij です。彼はスウェーデンのプログラマーでありゲーム愛好家で、2002 年に infoNES エミュレータを開発しました。infoNES は NES (Nintendo Entertainment System) ベースのエミュレータで、コンピュータでクラシック NES ゲームが遊べるように設計されています。

InfoNES は移植性が高く、環境関連のコンテンツをソフトウェア カーネルから削除し、単一の InfoNES_System.h に収集します。ここで説明したさまざまな関数を実装し、InfoNES Compile を一緒に追加する必要があります。プロジェクト。

最近、USB インターフェイスの FC ハンドル ドライバーが実装され、imx6ull 開発ボード上でゲームがプレイできるようになったので、その移植プロセスをここに記録します。NES エミュレータのソース コード実装に興味がある場合は、infoNES も優れた研究対象であり、コード構造が明確なので、古典的な CPU k6502 をシミュレートする方法を理解し、コンピュータ アーキテクチャへの理解を深めることができます。

次に、古典を再訪して、子供時代を思い出してみましょう。

池の外の失われた本では、ジニアオが夏を呼んでいます...美しい歌声とともに、タイムトラベルから戻ってきたようです、少年。

以下の操作を完了すると、すぐに携帯ゲーム機を所有し、子供の頃の夢を実現できます。

私は昔、imax283 プラットフォームに infoNES を移植しましたが、その当時の私の github ウェアハウスのアドレスは次のとおりでした。

https://github.com/yongzhena/infoNES

今回は、それをプルダウンして使用するだけで、完全に実行されるようにジョイパッド ハンドルによって駆動されるコードを変更するだけです。

移植プロセス

移植プロセス全体には、主にディスプレイ、サウンド出力、USB ハンドルのサポートの 3 つの部分が含まれます。最初の 2 つは、上記の鉱山を直接プルすることで直接入手できますが、ここでは USB コントローラー ドライバーのサポートに焦点を当てます。

fb0に基づくLCDディスプレイ

InfoNES_System_Linux.cpp ファイルを変更します。このブロックは 2 つの関数 (1 つは lcd_fb_init、もう 1 つは lcd_fb_display_px) を実装していることを示しています。

static int lcd_fb_init()
{
	//如果使用 mmap 打开方式 必须是 读定方式
	fb_fd = open("/dev/fb0", O_RDWR);
	if(-1 == fb_fd)
	{
		printf("cat't open /dev/fb0 \n");
		return -1;
	}
	//获取屏幕参数
	if(-1 == ioctl(fb_fd, FBIOGET_VSCREENINFO, &var))
	{
		close(fb_fd);
		printf("cat't ioctl /dev/fb0 \n");
		return -1;
	}
	
	//计算参数
	px_width     = var.bits_per_pixel /8;
	line_width   = var.xres * px_width;
	screen_width = var.yres * line_width;
	lcd_width    = var.xres;
	lcd_height   = var.yres;
	
	printf("fb width:%d height:%d pixel:%d \n", lcd_width, lcd_height,px_width*8);

	fb_mem = (unsigned char *)mmap(NULL, screen_width, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
	if(fb_mem == (void *)-1)
	{
		close(fb_fd);
		printf("cat't mmap /dev/fb0 \n");
		return -1;
	}
	//清屏
	memset(fb_mem, 0 , screen_width);
	return 0;
}
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;
}

以下の実装ではzoom_x_tabとzoom_y_tabの2つの項目に着目しています。その役割は、全画面表示を実行し、ピクセルを拡大することです。ソース コードの make_zoom_tab() はこの目的のためにあります。画面が大きくて拡大したときに粒状感が重いと感じた場合、最適化できますか? 考えられる最適化の方向性は次のとおりです。

/*===================================================================*/
/*                                                                   */
/*      InfoNES_LoadFrame() :                                        */
/*           Transfer the contents of work frame on the screen       */
/*                                                                   */
/*===================================================================*/
unsigned short ChColor(unsigned short color)
{
	return (color>>3)<<4|(color&0x001f);
}

void InfoNES_LoadFrame()
{
	int x,y;
	int line_width;
	WORD wColor,R,G,B,Gr;
	
	//修正 
	if(0 < fb_fd)
	{
		for (y = 0; y < lcd_height; y++ )
		{
			line_width = zoom_y_tab[y] * NES_DISP_WIDTH;
			for (x = 0; x < lcd_width; x++ )
			{
				wColor = ChColor(WorkFrame[line_width  + zoom_x_tab[x]]);
				lcd_fb_display_px(wColor, x, y);
			}
		}
	}
	
	  /*16 bit per pixel*/
	 /* Exchange 16-bit to 256 gray */
	 /*
     for (y = 0; y < NES_DISP_HEIGHT; y++ )
     {
         for (x = 0; x < NES_DISP_WIDTH; x++ )
         {
             //wColor = WorkFrame[y * lcd_width  + x ];
			  wColor = WorkFrame[ ( y << 8 ) + x ];
			  R = ( ( wColor & 0x7c00 ) >>7 );
              G = ( ( wColor & 0x03e0 ) >>2 );
              B = ( ( wColor & 0x001f ) <<3 );            
              //Gr= ( ( 9798*R + 19235*G + 3735*B)>>15);
              wColor=(WORD)((B<<16)|(G<<8)|R);
             lcd_fb_display_px(wColor, x, y);
         }
     }
	 */  
}

Alsa ベースのサウンドのサポート

このサウンドサポートを実現するには、ボード上に alsa フレームワークに基づくオーディオドライバーが存在し、機能が正常であることが前提となります。それ以外の場合、以下の実装はすべて空白のままにし、実装しない必要があります。

/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundInit() : Sound Emulation Initialize           */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundInit( void )
{
	
}


/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundOpen() : Sound Open                           */
/*                                                                   */
/*===================================================================*/
int InfoNES_SoundOpen( int samples_per_sync, int sample_rate )
{
	// sample_rate 采样率 44100
	// samples_per_sync  735
	// 采样率 / 8 * 声道数 = 44100 / 8 * 1 = 5512.5
	// 8位 声音
	/*
	声道数 1
    采样率 44100
    采样位数 8
    每次播放块大小(NES  APU 每次生成一块)735
	*/
	unsigned int rate      = sample_rate;
	snd_pcm_hw_params_t *hw_params;
	
	if(0 > snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) 
	{
		printf("snd_pcm_open err\n");
		return -1;
	}
	printf("snd_pcm_open ok!\nsamples_per_sync=%d,sample_rate=%d\n",samples_per_sync,sample_rate);
	
	if(0 > snd_pcm_hw_params_malloc(&hw_params))
	{
		printf("snd_pcm_hw_params_malloc err\n");
		return -1;
	}
	
	if(0 > snd_pcm_hw_params_any(playback_handle, hw_params))
	{
		printf("snd_pcm_hw_params_any err\n");
		return -1;
	}
	if(0 > snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) 
	{
		printf("snd_pcm_hw_params_any err\n");
		return -1;
	}

	//16bit PCM 数据
	
	if(0 > snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_U8))
	{
		printf("snd_pcm_hw_params_set_format err\n");
		return -1;
	}
	
	if(0 > snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &rate, 0)) 
	{
		printf("snd_pcm_hw_params_set_rate_near err\n");
		return -1;
	}

	//单声道 非立体声
	if(0 > snd_pcm_hw_params_set_channels(playback_handle, hw_params, 1))
	{
		printf("snd_pcm_hw_params_set_channels err\n");
		return -1;
	}

	if(0 > snd_pcm_hw_params(playback_handle, hw_params)) 
	{
		printf("snd_pcm_hw_params err\n");
		return -1;
	}
	
	snd_pcm_hw_params_free(hw_params);
	
	if(0 > snd_pcm_prepare(playback_handle)) 
	{
		printf("snd_pcm_prepare err\n");
		return -1;
	}
	
	return 1;
}


/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundClose() : Sound Close                         */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundClose( void )
{
	snd_pcm_close(playback_handle);
}


/*===================================================================*/
/*                                                                   */
/*            InfoNES_SoundOutput() : Sound Output 5 Waves           */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundOutput( int samples, BYTE *wave1, BYTE *wave2, BYTE *wave3, BYTE *wave4, BYTE *wave5 )
{
	
	int i;
	int ret;
	unsigned char wav;
	unsigned char *pcmBuf = (unsigned char *)malloc(samples);
	
	//printf("InfoNES_SoundOutput,samples=%d\n",samples);
	//printf("\n");
	for (i=0; i <samples; i++)
	{
		wav = (wave1[i] + wave2[i] + wave3[i] + wave4[i] + wave5[i]) / 5;
		//单声道 8位数据
		pcmBuf[i] = wav;
		//printf("%02x",wav);
	}
	//printf("\n");
	ret = snd_pcm_writei(playback_handle, pcmBuf, samples);
	if(-EPIPE == ret)
    {
        snd_pcm_prepare(playback_handle);
    }
	free(pcmBuf);
	return ;
}

USBコントローラーのサポート

次の部分は、USB コントローラ ドライバのサポートを実現するための導入の焦点です。こんな感じで遊べるんです。私が購入した USB ゲームパッドは安くて簡単に入手できます。お使いの USB ハンドルがこのタイプではない場合、ドライバー サポートの原則は同様で常に同じですが、キーと値の対応は私のものとは異なる可能性があります。実際のテスト後に変更してください。

USB ゲームパッドのドライバー サポートについては、私の以前のブログ投稿を参照してください: iMX6ULL ドライバー開発 | imx6ull 開発ボードに USB インターフェイスをサポートさせよう FC ゲームパッド_Maverick cat a のブログ - CSDN ブログ

上記で要約したようにカーネルを再コンパイルしたくない場合は、ドライバーをモジュールに個別にコンパイルし、そのモジュールに動的にロードできます。

ここでは、infoNES が USB コントローラーをサポートするためにどのような移植を行う必要があるかを紹介します。

ボタンキー値テストアプレット

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h> 


#define _EV_KEY         0x01    /* button pressed/released */
#define _EV_ABS         0x03    
#define _EV_MSC         0x04   

int main() {
    printf("hello,usb hid joystick key test\n");
    int fd = open("/dev/input/event3", O_RDONLY);
    struct input_event e;
    while(1) {
        read(fd, &e, sizeof(e));
        switch(e.type) {
            case _EV_KEY:
                printf("type: %d, code: %d,value: %d, time: %d\n", e.type, e.code,e.value, e.time);
                break;
            case _EV_ABS:
                printf("type: %d, code: %d,value: %d, time: %d\n", e.type, e.code,e.value, e.time);
                break;
            case _EV_MSC:
            printf("type: %d, code: %d,value: %d, time: %d\n", e.type, e.code,e.value, e.time);
            break;
            default:
                if(e.type != 0){
                printf("type:%d, code: %d,value: %d, time: %d\n",e.type, e.code,e.value, e.time);
                }
        }
    }
    close(fd);
    return 0;
}

Joypad_input.cpp ファイルの変更

これは主に USBjoypadGet() インターフェイスの実装であり、FC ハンドルのキー値に対応する必要があります。

static int USBjoypadGet(void)
{
	/**
	 * FC手柄 bit 键位对应关系 真实手柄中有一个定时器,处理 连A  连B 
	 * 0  1   2       3       4    5      6     7
	 * A  B   Select  Start  Up   Down   Left  Right
	 */
	//因为 USB 手柄每次只能读到一位键值 所以要有静态变量保存上一次的值
	static unsigned char joypad = 0;
	struct input_event e;
	if(0 < read (USBjoypad_fd, &e, sizeof(e)))
	{
		if(0x3 == e.type)
		{
			/*
			上:
			value:0 type:0x3 code:0x1
			value:127 type:0x3 code:0x1
			*/
			if(0 == e.value && 0x1 == e.code)
			{
				joypad |= 1<<4;
                printf("Up\n");
			}
			
			/*下:
			value:255 type:0x3 code:0x1
			value:127 type:0x3 code:0x1
			*/
			if(255 == e.value && 0x1 == e.code)
			{
				joypad |= 1<<5;
                printf("Down\n");
			}
			//松开
			if(127 == e.value && 0x1 == e.code)
			{
				joypad &= ~(1<<4 | 1<<5);
			}
			
			/*左:
			value:0 type:0x3 code:0x0
			value:127 type:0x3 code:0x0
			*/
			if(0 == e.value && 0 == e.code)
			{
				joypad |= 1<<6;
                printf("Left\n");
			}
			
			/*右:
			value:255 type:0x3 code:0x0
			value:127 type:0x3 code:0x0
			*/
			if(255 == e.value && 0 == e.code)
			{
				joypad |= 1<<7;
                printf("Right\n");
			}
			//松开
			if(127 == e.value && 0 == e.code)
			{
				joypad &= ~(1<<6 | 1<<7);
			}
		}

		if(0x1 == e.type)
		{
			/*选择:
			value:0x1 type:0x1 code:296
			value:0x0 type:0x1 code:296
			*/
			if(0x1 == e.value && 296 == e.code)
			{
				joypad |= 1<<2;
                printf("Select\n");
			}
			if(0x0 == e.value && 296 == e.code)
			{
				joypad &= ~(1<<2);
			}
			
			/*开始:
			value:0x1 type:0x1 code:297
			value:0x0 type:0x1 code:297
			*/
			if(0x1 == e.value && 297 == e.code)
			{
				joypad |= 1<<3;
                printf("Start\n");
			}
			if(0x0 == e.value && 297 == e.code)
			{
				joypad &= ~(1<<3);
			}

			/*A
			value:0x1 type:0x1 code:288
			value:0x0 type:0x1 code:288
			*/
			if(0x1 == e.value && 288 == e.code)
			{
				joypad |= 1<<0;
                printf("A\n");
			}
			if(0x0 == e.value && 288 == e.code)
			{
				joypad &= ~(1<<0);
			}

			/*B
			value:0x1 type:0x1 code:289
			value:0x0 type:0x1 code:289
			*/
			if(0x1 == e.value && 289 == e.code)
			{
				joypad |= 1<<1;
                printf("B\n");
			}
			if(0x0 == e.value && 289 == e.code)
			{
				joypad &= ~(1<<1);
			}

			/*X
			value:0x1 type:0x1 code:290
			value:0x0 type:0x1 code:290
			*/
			if(0x1 == e.value && 290 == e.code)
			{
				joypad |= 1<<0;
                printf("X\n");
			}
			if(0x0 == e.value && 290 == e.code)
			{
				joypad &= ~(1<<0);
			}

			/*Y
			value:0x1 type:0x1 code:291
			value:0x0 type:0x1 code:291
		 	*/
		 	if(0x1 == e.value && 291 == e.code)
			{
				joypad |= 1<<1;
                printf("Y\n");
			}
			if(0x0 == e.value && 291 == e.code)
			{
				joypad &= ~(1<<1);
			}
		}
		return joypad;
	}
	return -1;
}

完全に実現した

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <linux/input.h> 

#define JOYPAD_DEV "/dev/joypad"
#define USB_JS_DEV "/dev/input/event3"


typedef struct JoypadInput{
	int (*DevInit)(void);
	int (*DevExit)(void);
	int (*GetJoypad)(void);
	struct JoypadInput *ptNext;
	pthread_t tTreadID;     /* 子线程ID */
}T_JoypadInput, *PT_JoypadInput;

//全局变量通过互斥体访问
static unsigned char g_InputEvent;

static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER;

static int joypad_fd;
static int USBjoypad_fd;
static PT_JoypadInput g_ptJoypadInputHead;


static void *InputEventTreadFunction(void *pVoid)
{
	/* 定义函数指针 */
	int (*GetJoypad)(void);
	GetJoypad = (int (*)(void))pVoid;

	while (1)
	{
		//因为有阻塞所以没有输入时是休眠
		g_InputEvent = GetJoypad();
		//有数据时唤醒
		pthread_mutex_lock(&g_tMutex);
		/*  唤醒主线程 */
		pthread_cond_signal(&g_tConVar);
		pthread_mutex_unlock(&g_tMutex);
	}
}

static int RegisterJoypadInput(PT_JoypadInput ptJoypadInput)
{
	PT_JoypadInput tmp;
	if(ptJoypadInput->DevInit())
	{
		return -1;
	}
	//初始化成功创建子线程 将子项的GetInputEvent 传进来
	pthread_create(&ptJoypadInput->tTreadID, NULL, InputEventTreadFunction, (void*)ptJoypadInput->GetJoypad);
	if(! g_ptJoypadInputHead)
	{
		g_ptJoypadInputHead = ptJoypadInput;
	}
	else
	{
		tmp = g_ptJoypadInputHead;
		while(tmp->ptNext)
		{
			tmp = tmp->ptNext;
		}
		tmp->ptNext = ptJoypadInput;
	}
	ptJoypadInput->ptNext = NULL;
	return 0;
}

static int joypadGet(void)
{
	static unsigned char joypad = 0;
	//printf("joypadGet val:\n");
	joypad = read(joypad_fd, 0, 0);
	return joypad;
}

static int joypadDevInit(void)
{
	joypad_fd = open(JOYPAD_DEV, O_RDONLY);
	if(-1 == joypad_fd)
	{
		printf("%s dev not found \r\n", JOYPAD_DEV);
		return -1;
	}
	return 0;
}

static int joypadDevExit(void)
{
	close(joypad_fd);
	return 0;
}

static T_JoypadInput joypadInput = {
	joypadDevInit,
	joypadDevExit,
	joypadGet,
};

static int USBjoypadGet(void)
{
	/**
	 * FC手柄 bit 键位对应关系 真实手柄中有一个定时器,处理 连A  连B 
	 * 0  1   2       3       4    5      6     7
	 * A  B   Select  Start  Up   Down   Left  Right
	 */
	//因为 USB 手柄每次只能读到一位键值 所以要有静态变量保存上一次的值
	static unsigned char joypad = 0;
	struct input_event e;
	if(0 < read (USBjoypad_fd, &e, sizeof(e)))
	{
		if(0x3 == e.type)
		{
			/*
			上:
			value:0 type:0x3 code:0x1
			value:127 type:0x3 code:0x1
			*/
			if(0 == e.value && 0x1 == e.code)
			{
				joypad |= 1<<4;
                printf("Up\n");
			}
			
			/*下:
			value:255 type:0x3 code:0x1
			value:127 type:0x3 code:0x1
			*/
			if(255 == e.value && 0x1 == e.code)
			{
				joypad |= 1<<5;
                printf("Down\n");
			}
			//松开
			if(127 == e.value && 0x1 == e.code)
			{
				joypad &= ~(1<<4 | 1<<5);
			}
			
			/*左:
			value:0 type:0x3 code:0x0
			value:127 type:0x3 code:0x0
			*/
			if(0 == e.value && 0 == e.code)
			{
				joypad |= 1<<6;
                printf("Left\n");
			}
			
			/*右:
			value:255 type:0x3 code:0x0
			value:127 type:0x3 code:0x0
			*/
			if(255 == e.value && 0 == e.code)
			{
				joypad |= 1<<7;
                printf("Right\n");
			}
			//松开
			if(127 == e.value && 0 == e.code)
			{
				joypad &= ~(1<<6 | 1<<7);
			}
		}

		if(0x1 == e.type)
		{
			/*选择:
			value:0x1 type:0x1 code:296
			value:0x0 type:0x1 code:296
			*/
			if(0x1 == e.value && 296 == e.code)
			{
				joypad |= 1<<2;
                printf("Select\n");
			}
			if(0x0 == e.value && 296 == e.code)
			{
				joypad &= ~(1<<2);
			}
			
			/*开始:
			value:0x1 type:0x1 code:297
			value:0x0 type:0x1 code:297
			*/
			if(0x1 == e.value && 297 == e.code)
			{
				joypad |= 1<<3;
                printf("Start\n");
			}
			if(0x0 == e.value && 297 == e.code)
			{
				joypad &= ~(1<<3);
			}

			/*A
			value:0x1 type:0x1 code:288
			value:0x0 type:0x1 code:288
			*/
			if(0x1 == e.value && 288 == e.code)
			{
				joypad |= 1<<0;
                printf("A\n");
			}
			if(0x0 == e.value && 288 == e.code)
			{
				joypad &= ~(1<<0);
			}

			/*B
			value:0x1 type:0x1 code:289
			value:0x0 type:0x1 code:289
			*/
			if(0x1 == e.value && 289 == e.code)
			{
				joypad |= 1<<1;
                printf("B\n");
			}
			if(0x0 == e.value && 289 == e.code)
			{
				joypad &= ~(1<<1);
			}

			/*X
			value:0x1 type:0x1 code:290
			value:0x0 type:0x1 code:290
			*/
			if(0x1 == e.value && 290 == e.code)
			{
				joypad |= 1<<0;
                printf("X\n");
			}
			if(0x0 == e.value && 290 == e.code)
			{
				joypad &= ~(1<<0);
			}

			/*Y
			value:0x1 type:0x1 code:291
			value:0x0 type:0x1 code:291
		 	*/
		 	if(0x1 == e.value && 291 == e.code)
			{
				joypad |= 1<<1;
                printf("Y\n");
			}
			if(0x0 == e.value && 291 == e.code)
			{
				joypad &= ~(1<<1);
			}
		}
		return joypad;
	}
	return -1;
}

static int USBjoypadDevInit(void)
{
	USBjoypad_fd = open(USB_JS_DEV, O_RDONLY);
	if(-1 == USBjoypad_fd)
	{
		printf("%s dev not found \r\n", USB_JS_DEV);
		return -1;
	}
	return 0;
}

static int USBjoypadDevExit(void)
{
	close(USBjoypad_fd);
	return 0;
}

static T_JoypadInput usbJoypadInput = {
	USBjoypadDevInit,
	USBjoypadDevExit,
	USBjoypadGet,
};

int InitJoypadInput(void)
{
	int iErr = 0;
	//iErr = RegisterJoypadInput(&joypadInput);
	iErr = RegisterJoypadInput(&usbJoypadInput);
	return iErr;
}

int GetJoypadInput(void)
{
	/* 休眠 */
	pthread_mutex_lock(&g_tMutex);
	pthread_cond_wait(&g_tConVar, &g_tMutex);	

	/* 被唤醒后,返回数据 */
	pthread_mutex_unlock(&g_tMutex);
	return g_InputEvent;
}

コンパイルと生成

最後に、クロスコンパイルして実行可能ファイルを生成し、それをボードに配置して実行し、USB ハンドルを接続して再生すると、正常に動作します。まだ非常に滑らかです。サウンドをサポートするために、alsa ヘッダー ファイルが使用され、libasound ライブラリがリンクされることに注意してください。ご使用の環境にこのライブラリがあることを確認してください。そうでない場合は、サウンド出力がサポートされないため、このリンクを削除してください。記事の最後にはNESゲームのROMリソースがあります。

makefile脚本

#根据实际路径修改工具链路径
CHAIN_ROOT=/opt/yang/imax6ul/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin
CROSS_COMPILE=$(CHAIN_ROOT)/arm-linux-gnueabihf-

#CHAIN_ROOT= /home/yang/b503/ctools/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin
#CROSS_COMPILE=$(CHAIN_ROOT)/arm-linux-gnueabihf-
#CROSS_COMPILE = 

CC     := $(CROSS_COMPILE)gcc
#CC = arm-poky-linux-gnueabi-gcc
TARBALL = InfoNES08J

# InfoNES
.CFILES =	./../K6502.cpp \
		./../InfoNES.cpp \
		./../InfoNES_Mapper.cpp \
		./../InfoNES_pAPU.cpp \
		./InfoNES_System_Linux.cpp joypad_input.cpp

.OFILES	=	$(.CFILES:.cpp=.o)

CCFLAGS =    -o2 -fsigned-char  -I../
LDFILGS = -lstdc++	-L../libs	# gcc3.x.x

all: InfoNES

InfoNES: $(.OFILES)
	$(CC) $(INCLUDES) -o $@ $(.OFILES) $(LDFILGS) -lm  -lpthread -lasound

.cpp.o:
	$(CC) $(INCLUDES) -c $(CCFLAGS) $*.cpp  -o $@

clean:
	rm -f $(.OFILES) ../*~ ../*/*~ core

cleanall:
	rm -f $(.OFILES) ../*~ ../*/*~ core InfoNES

release: clean all

tar:
	( cd ..; \
	tar cvf $(TARBALL).tar ./*; \
	gzip $(TARBALL).tar \
	)

install:
	install ./InfoNES /usr/local/bin

その他のリソース

ファミコン赤白機全画面表示

NES トピック - NES ゲームコンソールの紹介_nesfc_Jin Xiaoting のブログ-CSDN ブログ

V3S 移植版 NES ゲーム シミュレーター (ゲーム コレクション付き)_v3s コンパイル済みゲーム シミュレーター_qq_46604211 のブログ - CSDN ブログ

任天堂赤白機ファミコンゲーム紹介 任天堂赤白機ファミコンゲーム紹介

情報: 多くの NES ゲーム ROM ファイルと実行中のエミュレータが含まれています

リンク: https://pan.baidu.com/s/1uXAxLKGmKGwZFB3Yraq8gg 抽出コード: qxcy 

ゲームを収集して解凍し、ゲーム名を英語に変更しました
リンク: https://pan.baidu.com/s/16hIWwYQQEX9aOBDG1dVa0A
抽出コード: asdf

おすすめ

転載: blog.csdn.net/qq8864/article/details/132085917