iMX6ULL应用移植 | 移植 infoNES 模拟器(重玩经典NES游戏)

没玩过NES游戏的童年,可能不是80后的童年。我们小时候是从玩FC开始接触游戏机的,那时真的是红极一时啊,我上初中时还省吃俭用买了一台小霸王,暑假里把电视机都给打爆了!那时任天堂单是FC机的主机的发售收入就超过全美的电视台的收入的总和,在人们的心目中扎下了任天堂的这个招牌。

前言

1983年7月15日,由日本任天堂株式会社(原本是生产日式扑克即“花札”)的宫本茂先生领导开发的一种第三代家用电子游戏机:FC,全称:Family Computer,也称作:Famicom;在欧美发售时则被称为nes,全称:Nintendo Entertainment System;在中国大陆、台湾和香港等地,因其外壳为红白两色,所以人们俗称其为“红白机”,正式进入市场销售,并于后来取得了巨大成功,由此揭开了家用电子游戏机遍布世界任何角落,电子游戏全球大普及的序幕。

iMX6ULL应用移植 | 移植 infoNES 模拟器(重玩经典NES游戏)_第1张图片

什么是InfNES?

一款NES游戏模拟器。InfoNES可以很容易地被移植到各个平台,作者是Martin Freij。他是一位瑞典的程序员和游戏爱好者,于2002年开发了infoNES模拟器。infoNES是一个基于NES(任天堂娱乐系统)的模拟器,旨在让人们能够在计算机上玩经典的NES游戏。

InfoNES具备良好的可移植性,它将与环境有关的内容都清出了软件内核,并且单独集合于一个InfoNES_System.h中,我们要做的就是实现这里提到的各种函数,再把InfoNES加入到我们的工程中一起编译。

最近成功实现了USB接口的FC手柄驱动,使得在imx6ull开发板玩游戏具有可玩性,这里将这个移植过程记录下来。如果对NES模拟器的源码实现感兴趣,infoNES也是个不错的研究对象,代码结构清晰,可以让你了解到如何模拟实现k6502这款经典cpu的,加深对计算机体系结构的理解。

接下来让我们重温下经典,缅怀下童年吧!

池塘外的迷路书上,知鸟在声声叫着夏天......,伴随着优美的歌声,仿佛穿越回来了,少年。

完成以下操作,让你即刻拥有款移动游戏机,实现童年时的梦想。

很早之前我在imax283平台上移植过infoNES,那时我的github仓地址是:

https://github.com/yongzhena/infoNES

这次直接拉取下来用,只是修改下joypad手柄驱动的代码就可以完美运行啦。

iMX6ULL应用移植 | 移植 infoNES 模拟器(重玩经典NES游戏)_第2张图片

移植过程

整个移植过程主要涉及三部分,显示、声音输出和usb手柄支持。前两个直接拉取上面的我的仓直接就具备了,这里着重介绍下USB手柄驱动支持。

基于fb0的LCD显示

在InfoNES_System_Linux.cpp文件中修改。显示这块儿实现两个函数,一个是lcd_fb_init,一个是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这两项。它的作用是对像素做了全屏和放大处理。 源码里的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 

USB手柄支持

接下来这块儿是介绍的重点,实现usb手柄驱动的支持。这样才有可玩性啊。我买的这款USB的游戏手柄很便宜,也很容易买到。如果你的USB手柄不是这款,那么实现驱动支持的原理也是类似的,万变不离宗,只是键值对应关系跟我的可能不一样,实测改下即可。

关于USB游戏手柄的驱动支持,参见我的上篇博文:iMX6ULL驱动开发 | 让imx6ull开发板支持usb接口FC游戏手柄_特立独行的猫a的博客-CSDN博客

不想按上文总结的重新编译内核的话,可以把驱动单独编译成模块动态加载进去。

这里介绍下让infoNES支持usb手柄需要做哪些移植。

按键键值测试小程序

#include 
#include 
#include 
#include 
#include  


#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 
#include 
#include 
#include 
#include 
#include 
#include  

#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专题——NES游戏机简介_nesfc_金小庭的博客-CSDN博客

V3S移植nes游戏模拟器(附带游戏合集)_v3s编译游戏模拟器_qq_46604211的博客-CSDN博客

任天堂红白机nes游戏简介 任天堂红白机nes游戏简介

资料:内含众多NES的游戏ROM文件及运行模拟器

链接:https://pan.baidu.com/s/1uXAxLKGmKGwZFB3Yraq8gg  提取码:qxcy 

游戏合集并解压,然后改名为游戏名为英文
链接:https://pan.baidu.com/s/16hIWwYQQEX9aOBDG1dVa0A
提取码:asdf

你可能感兴趣的:(游戏,NES模拟器,infoNES,移植)