nes 红白机模拟器 第5篇 全屏显示

时间:2022-09-14 08:38:29

先看一下效果图

nes 红白机模拟器 第5篇 全屏显示

nes 红白机模拟器 第5篇 全屏显示

nes 红白机模拟器 第5篇 全屏显示

放大的原理是使用最初级的算法,直接取对应像素法。

 /*===================================================================*/
/* */
/* InfoNES_System_Linux.cpp : Linux specific File */
/* */
/* 2001/05/18 InfoNES Project ( Sound is based on DarcNES ) */
/* */
/*===================================================================*/ /*-------------------------------------------------------------------*/
/* Include files */
/*-------------------------------------------------------------------*/ /**
* author:ningci dev
* date:2017-04-24 21:23
*/ #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h> #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/soundcard.h> #include "../InfoNES.h"
#include "../InfoNES_System.h"
#include "../InfoNES_pAPU.h" //bool define
#define TRUE 1
#define FALSE 0 /* lcd 操作相关 头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <termios.h> #include <fcntl.h> #define JOYPAD_DEV "/dev/joypad"
static int joypad_fd; static int fb_fd;
static unsigned char *fb_mem;
static int px_width;
static int line_width;
static int screen_width;
static int lcd_width;
static int lcd_height;
static struct fb_var_screeninfo var; static int *zoom_x_tab;
static int *zoom_y_tab; static int init_joypad()
{
joypad_fd = open(JOYPAD_DEV, O_RDONLY);
if(- == joypad_fd)
{
printf("joypad dev not found \r\n");
return -;
}
return ;
} 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 ;
} static int lcd_fb_init()
{
//如果使用 mmap 打开方式 必须是 读定方式
fb_fd = open("/dev/fb0", O_RDWR);
if(- == fb_fd)
{
printf("cat't open /dev/fb0 \n");
return -;
}
//获取屏幕参数
if(- == ioctl(fb_fd, FBIOGET_VSCREENINFO, &var))
{
close(fb_fd);
printf("cat't ioctl /dev/fb0 \n");
return -;
} //计算参数
px_width = var.bits_per_pixel / ;
line_width = var.xres * px_width;
screen_width = var.yres * line_width;
lcd_width = var.xres;
lcd_height = var.yres; fb_mem = (unsigned char *)mmap(NULL, screen_width, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, );
if(fb_mem == (void *)-)
{
close(fb_fd);
printf("cat't mmap /dev/fb0 \n");
return -;
}
//清屏
memset(fb_mem, , screen_width);
return ;
} 124 /**
125 * 生成zoom 缩放表
126 */
127 int make_zoom_tab()
{
int i;
zoom_x_tab = (int *)malloc(sizeof(int) * lcd_width);
if(NULL == zoom_x_tab)
{
printf("make zoom_x_tab error\n");
return -;
}
for(i=; i<lcd_width; i++)
{
zoom_x_tab[i] = (i+0.4999999)*NES_DISP_WIDTH/lcd_width-0.5;
}
zoom_y_tab = (int *)malloc(sizeof(int) * lcd_height);
if(NULL == zoom_y_tab)
{
printf("make zoom_y_tab error\n");
return -;
}
for(i=; i<lcd_height; i++)
{
zoom_y_tab[i] = (i+0.4999999)*NES_DISP_HEIGHT/lcd_height-0.5;
}
return ;
} /*-------------------------------------------------------------------*/
/* ROM image file information */
/*-------------------------------------------------------------------*/ char szRomName[];
char szSaveName[];
int nSRAM_SaveFlag; /*-------------------------------------------------------------------*/
/* Constants ( Linux specific ) */
/*-------------------------------------------------------------------*/ #define VBOX_SIZE 7
#define SOUND_DEVICE "/dev/dsp"
#define VERSION "InfoNES v0.91J" /*-------------------------------------------------------------------*/
/* Global Variables ( Linux specific ) */
/*-------------------------------------------------------------------*/ /* Emulation thread */
pthread_t emulation_tid;
int bThread; /* Pad state */
DWORD dwKeyPad1;
DWORD dwKeyPad2;
DWORD dwKeySystem; /* For Sound Emulation */
BYTE final_wave[];
int waveptr;
int wavflag;
int sound_fd; /*-------------------------------------------------------------------*/
/* Function prototypes ( Linux specific ) */
/*-------------------------------------------------------------------*/ void *emulation_thread( void *args ); void start_application( char *filename ); int LoadSRAM(); int SaveSRAM(); /* Palette data */
WORD NesPalette[] =
{
0x39ce, 0x1071, 0x0015, 0x2013, 0x440e, 0x5402, 0x5000, 0x3c20,
0x20a0, 0x0100, 0x0140, 0x00e2, 0x0ceb, 0x0000, 0x0000, 0x0000,
0x5ef7, 0x01dd, 0x10fd, 0x401e, 0x5c17, 0x700b, 0x6ca0, 0x6521,
0x45c0, 0x0240, 0x02a0, 0x0247, 0x0211, 0x0000, 0x0000, 0x0000,
0x7fff, 0x1eff, 0x2e5f, 0x223f, 0x79ff, 0x7dd6, 0x7dcc, 0x7e67,
0x7ae7, 0x4342, 0x2769, 0x2ff3, 0x03bb, 0x0000, 0x0000, 0x0000,
0x7fff, 0x579f, 0x635f, 0x6b3f, 0x7f1f, 0x7f1b, 0x7ef6, 0x7f75,
0x7f94, 0x73f4, 0x57d7, 0x5bf9, 0x4ffe, 0x0000, 0x0000, 0x0000
}; /*===================================================================*/
/* */
/* main() : Application main */
/* */
/*===================================================================*/ /* Application main */
int main( int argc, char **argv )
{
char cmd; /*-------------------------------------------------------------------*/
/* Pad Control */
/*-------------------------------------------------------------------*/ /* Initialize a pad state */
dwKeyPad1 = ;
dwKeyPad2 = ;
dwKeySystem = ; /*-------------------------------------------------------------------*/
/* Load Cassette & Create Thread */
/*-------------------------------------------------------------------*/ /* Initialize thread state */
bThread = FALSE; /* If a rom name specified, start it */
if ( argc == )
{
start_application( argv[] );
} lcd_fb_init();
init_joypad();
//初始化 zoom 缩放表
make_zoom_tab(); //主循环中处理输入事件
while()
{
if( < joypad_fd)
{
dwKeyPad1 = read(joypad_fd, , );
}
}
return();
} /*===================================================================*/
/* */
/* emulation_thread() : Thread Hooking Routine */
/* */
/*===================================================================*/ void *emulation_thread( void *args )
{
InfoNES_Main();
} /*===================================================================*/
/* */
/* start_application() : Start NES Hardware */
/* */
/*===================================================================*/
void start_application( char *filename )
{
/* Set a ROM image name */
strcpy( szRomName, filename ); /* Load cassette */
if ( InfoNES_Load( szRomName ) == )
{
/* Load SRAM */
LoadSRAM(); /* Create Emulation Thread */
bThread = TRUE;
pthread_create( &emulation_tid, NULL, emulation_thread, NULL );
}
} /*===================================================================*/
/* */
/* LoadSRAM() : Load a SRAM */
/* */
/*===================================================================*/
int LoadSRAM()
{
/*
* Load a SRAM
*
* Return values
* 0 : Normally
* -1 : SRAM data couldn't be read
*/ FILE *fp;
unsigned char pSrcBuf[SRAM_SIZE];
unsigned char chData;
unsigned char chTag;
int nRunLen;
int nDecoded;
int nDecLen;
int nIdx; /* It doesn't need to save it */
nSRAM_SaveFlag = ; /* It is finished if the ROM doesn't have SRAM */
if ( !ROM_SRAM )
return(); /* There is necessity to save it */
nSRAM_SaveFlag = ; /* The preparation of the SRAM file name */
strcpy( szSaveName, szRomName );
strcpy( strrchr( szSaveName, '.' ) + , "srm" ); /*-------------------------------------------------------------------*/
/* Read a SRAM data */
/*-------------------------------------------------------------------*/ /* Open SRAM file */
fp = fopen( szSaveName, "rb" );
if ( fp == NULL )
return(-); /* Read SRAM data */
fread( pSrcBuf, SRAM_SIZE, , fp ); /* Close SRAM file */
fclose( fp ); /*-------------------------------------------------------------------*/
/* Extract a SRAM data */
/*-------------------------------------------------------------------*/ nDecoded = ;
nDecLen = ; chTag = pSrcBuf[nDecoded++]; while ( nDecLen < )
{
chData = pSrcBuf[nDecoded++]; if ( chData == chTag )
{
chData = pSrcBuf[nDecoded++];
nRunLen = pSrcBuf[nDecoded++];
for ( nIdx = ; nIdx < nRunLen + ; ++nIdx )
{
SRAM[nDecLen++] = chData;
}
}else {
SRAM[nDecLen++] = chData;
}
} /* Successful */
return();
} /*===================================================================*/
/* */
/* SaveSRAM() : Save a SRAM */
/* */
/*===================================================================*/
int SaveSRAM()
{
/*
* Save a SRAM
*
* Return values
* 0 : Normally
* -1 : SRAM data couldn't be written
*/ FILE *fp;
int nUsedTable[];
unsigned char chData;
unsigned char chPrevData;
unsigned char chTag;
int nIdx;
int nEncoded;
int nEncLen;
int nRunLen;
unsigned char pDstBuf[SRAM_SIZE]; if ( !nSRAM_SaveFlag )
return(); /* It doesn't need to save it */ /*-------------------------------------------------------------------*/
/* Compress a SRAM data */
/*-------------------------------------------------------------------*/ memset( nUsedTable, , sizeof nUsedTable ); for ( nIdx = ; nIdx < SRAM_SIZE; ++nIdx )
{
++nUsedTable[SRAM[nIdx++]];
}
for ( nIdx = , chTag = ; nIdx < ; ++nIdx )
{
if ( nUsedTable[nIdx] < nUsedTable[chTag] )
chTag = nIdx;
} nEncoded = ;
nEncLen = ;
nRunLen = ; pDstBuf[nEncLen++] = chTag; chPrevData = SRAM[nEncoded++]; while ( nEncoded < SRAM_SIZE && nEncLen < SRAM_SIZE - )
{
chData = SRAM[nEncoded++]; if ( chPrevData == chData && nRunLen < )
++nRunLen;
else{
if ( nRunLen >= || chPrevData == chTag )
{
pDstBuf[nEncLen++] = chTag;
pDstBuf[nEncLen++] = chPrevData;
pDstBuf[nEncLen++] = nRunLen - ;
}else {
for ( nIdx = ; nIdx < nRunLen; ++nIdx )
pDstBuf[nEncLen++] = chPrevData;
} chPrevData = chData;
nRunLen = ;
}
}
if ( nRunLen >= || chPrevData == chTag )
{
pDstBuf[nEncLen++] = chTag;
pDstBuf[nEncLen++] = chPrevData;
pDstBuf[nEncLen++] = nRunLen - ;
}else {
for ( nIdx = ; nIdx < nRunLen; ++nIdx )
pDstBuf[nEncLen++] = chPrevData;
} /*-------------------------------------------------------------------*/
/* Write a SRAM data */
/*-------------------------------------------------------------------*/ /* Open SRAM file */
fp = fopen( szSaveName, "wb" );
if ( fp == NULL )
return(-); /* Write SRAM data */
fwrite( pDstBuf, nEncLen, , fp ); /* Close SRAM file */
fclose( fp ); /* Successful */
return();
} /*===================================================================*/
/* */
/* InfoNES_Menu() : Menu screen */
/* */
/*===================================================================*/
int InfoNES_Menu()
{
/*
* Menu screen
*
* Return values
* 0 : Normally
* -1 : Exit InfoNES
*/ /* If terminated */
if ( bThread == FALSE )
{
return(-);
} /* Nothing to do here */
return();
} /*===================================================================*/
/* */
/* InfoNES_ReadRom() : Read ROM image file */
/* */
/*===================================================================*/
int InfoNES_ReadRom( const char *pszFileName )
{
/*
* Read ROM image file
*
* Parameters
* const char *pszFileName (Read)
*
* Return values
* 0 : Normally
* -1 : Error
*/ FILE *fp; /* Open ROM file */
fp = fopen( pszFileName, "rb" );
if ( fp == NULL )
return(-); /* Read ROM Header */
fread( &NesHeader, sizeof NesHeader, , fp );
if ( memcmp( NesHeader.byID, "NES\x1a", ) != )
{
/* not .nes file */
fclose( fp );
return(-);
} /* Clear SRAM */
memset( SRAM, , SRAM_SIZE ); /* If trainer presents Read Triner at 0x7000-0x71ff */
if ( NesHeader.byInfo1 & )
{
fread( &SRAM[0x1000], , , fp );
} /* Allocate Memory for ROM Image */
ROM = (BYTE *) malloc( NesHeader.byRomSize * 0x4000 ); /* Read ROM Image */
fread( ROM, 0x4000, NesHeader.byRomSize, fp ); if ( NesHeader.byVRomSize > )
{
/* Allocate Memory for VROM Image */
VROM = (BYTE *) malloc( NesHeader.byVRomSize * 0x2000 ); /* Read VROM Image */
fread( VROM, 0x2000, NesHeader.byVRomSize, fp );
} /* File close */
fclose( fp ); /* Successful */
return();
} /*===================================================================*/
/* */
/* InfoNES_ReleaseRom() : Release a memory for ROM */
/* */
/*===================================================================*/
void InfoNES_ReleaseRom()
{
/*
* Release a memory for ROM
*
*/ if ( ROM )
{
free( ROM );
ROM = NULL;
} if ( VROM )
{
free( VROM );
VROM = NULL;
}
} /*===================================================================*/
/* */
/* InfoNES_MemoryCopy() : memcpy */
/* */
/*===================================================================*/
void *InfoNES_MemoryCopy( void *dest, const void *src, int count )
{
/*
* memcpy
*
* Parameters
* void *dest (Write)
* Points to the starting address of the copied block's destination
*
* const void *src (Read)
* Points to the starting address of the block of memory to copy
*
* int count (Read)
* Specifies the size, in bytes, of the block of memory to copy
*
* Return values
* Pointer of destination
*/ memcpy( dest, src, count );
return(dest);
} /*===================================================================*/
/* */
/* InfoNES_MemorySet() : memset */
/* */
/*===================================================================*/
void *InfoNES_MemorySet( void *dest, int c, int count )
{
/*
* memset
*
* Parameters
* void *dest (Write)
* Points to the starting address of the block of memory to fill
*
* int c (Read)
* Specifies the byte value with which to fill the memory block
*
* int count (Read)
* Specifies the size, in bytes, of the block of memory to fill
*
* Return values
* Pointer of destination
*/ memset( dest, c, count );
return(dest);
} /*===================================================================*/
/* */
/* InfoNES_LoadFrame() : */
/* Transfer the contents of work frame on the screen */
/* */
/*===================================================================*/
void InfoNES_LoadFrame()
{
#if 0
int x,y;
WORD wColor;
for (y = ; y < NES_DISP_HEIGHT; y++ )
{
for (x = ; x < NES_DISP_WIDTH; x++ )
{
wColor = WorkFrame[y * NES_DISP_WIDTH + x ];
lcd_fb_display_px(wColor, x, y);
}
}
#else
686 int x,y;
687 WORD wColor;
688 for (y = 0; y < lcd_height; y++ )
689 {
690 for (x = 0; x < lcd_width; x++ )
691 {
692 wColor = WorkFrame[zoom_y_tab[y] * NES_DISP_WIDTH + zoom_x_tab[x]];
693 lcd_fb_display_px(wColor, x, y);
694 }
695 }
#endif
} /*===================================================================*/
/* */
/* InfoNES_PadState() : Get a joypad state */
/* */
/*===================================================================*/
void InfoNES_PadState( DWORD *pdwPad1, DWORD *pdwPad2, DWORD *pdwSystem )
{
/*
* Get a joypad state
*
* Parameters
* DWORD *pdwPad1 (Write)
* Joypad 1 State
*
* DWORD *pdwPad2 (Write)
* Joypad 2 State
*
* DWORD *pdwSystem (Write)
* Input for InfoNES
*
*/ /* Transfer joypad state */
*pdwPad1 = dwKeyPad1;
*pdwPad2 = dwKeyPad2;
*pdwSystem = dwKeySystem; dwKeyPad1 = ;
} /*===================================================================*/
/* */
/* InfoNES_SoundInit() : Sound Emulation Initialize */
/* */
/*===================================================================*/
void InfoNES_SoundInit( void )
{
sound_fd = ;
} /*===================================================================*/
/* */
/* InfoNES_SoundOpen() : Sound Open */
/* */
/*===================================================================*/
int InfoNES_SoundOpen( int samples_per_sync, int sample_rate )
{
return ;
} /*===================================================================*/
/* */
/* InfoNES_SoundClose() : Sound Close */
/* */
/*===================================================================*/
void InfoNES_SoundClose( void )
{
if ( sound_fd )
{
close( sound_fd );
}
} /*===================================================================*/
/* */
/* InfoNES_SoundOutput() : Sound Output 5 Waves */
/* */
/*===================================================================*/
void InfoNES_SoundOutput( int samples, BYTE *wave1, BYTE *wave2, BYTE *wave3, BYTE *wave4, BYTE *wave5 )
{ } /*===================================================================*/
/* */
/* InfoNES_Wait() : Wait Emulation if required */
/* */
/*===================================================================*/
void InfoNES_Wait()
{
} /*===================================================================*/
/* */
/* InfoNES_MessageBox() : Print System Message */
/* */
/*===================================================================*/
void InfoNES_MessageBox( char *pszMsg, ... )
{
printf( "MessageBox: %s \n", pszMsg );
} /*
* End of InfoNES_System_Linux.cpp
*/

发另外一个高级点的放大算法 - 二次线性插值缩放算法

发算法核心部分,原理是,取原图相邻的4个橡素,算权重。 经过测试效率非常低(每秒一两帧的样子,当然也未做优化)

 //得到指定位置的像素颜色
static int get_pixel(unsigned short *src_fb, int x, int y, int line_len, unsigned short *color)
{
int src_height = NES_DISP_HEIGHT;
int src_width = NES_DISP_WIDTH;
if(y>src_height || x>src_width)
{
*color = ;
return ;
}
*color = src_fb[line_len * y + x];
} static unsigned short color_avg(unsigned short color1, unsigned short color2, unsigned short color3, unsigned short color4)
{
unsigned short newColor;
unsigned int r,g,b;
//
r = ((color1>>)&0x1f + (color2>>)&0x1f + (color3>>)&0x1f + (color4>>)&0x1f)/;
g = ((color1>>)&0x3f + (color2>>)&0x3f + (color3>>)&0x3f + (color4>>)&0x3f) /;
b = ((color1>>)&0x1f + (color2>>)&0x1f + (color3>>)&0x1f + (color4>>)&0x1f) /;
newColor = ((r<<) | (g<<) | (b));
return newColor;
} //使用 二次线性插值 实现画面放大
//说明 目前仅支持 16bpp 的LCD 显示
//其它的还要修改兼容
static void lcd_draw()
{
int x;
int y;
float srcx;
float srcy;
int dst_y_line;
int src_height = NES_DISP_HEIGHT;
int src_width = NES_DISP_WIDTH;
int dst_height = lcd_height;
int dst_width = lcd_width;
unsigned short *src_fb = nes_disp_fb;
unsigned short *dst_fb = zoom_disp_fb;
int line_len = src_width; //Y坐标邻近的4个像素
unsigned short color1;
unsigned short color2;
unsigned short color3;
unsigned short color4; //算法说明
//1,取出目标XY坐标邻近的4个像素
for(y=; y<dst_height; y++)
{
srcy = (y+0.49)*src_height/dst_height-0.5;
srcy = int(srcy);
dst_y_line = y*lcd_width;
for(x=; x<dst_width; x++)
{
srcx = (x+0.49)*src_width/dst_width-0.5;
srcx = int(srcx);
get_pixel(src_fb, srcx, srcy, line_len, &color1);
get_pixel(src_fb, srcx, srcy+, line_len, &color2);
get_pixel(src_fb, srcx+, srcy, line_len, &color3);
get_pixel(src_fb, srcx+, srcy+, line_len, &color4);
//dst_fb[dst_y_line+x] = color_avg(color1, color2, color3, color4);
dst_fb[dst_y_line+x] = color1;
}
}
//复制到 fb
memcpy(fb_mem, dst_fb, screen_width);
}