完结篇……更新至2013.10.30 - 22:17
温馨提示,本文中每个部分涉及到的工程包括源文件,都可以在本文的最后发现下载链接哟!
前三部分链接:https://bbs.eeworld.com.cn/thread-415692-1-1.html
第四部分上链接:https://bbs.eeworld.com.cn/thread-416564-1-1.html
第四部分下链接:https://bbs.eeworld.com.cn/thread-416756-1-1.html
有童鞋问了,神马是InfNES?
这是一款NES游戏模拟器,也就是任天堂的红白机,80后的童年……InfoNES可以很容易地被移植到各个平台,可以说各大开发论坛上都可见它的身影。
如果你是有关技术的老鸟,那不好意思你可能什么都学不到了,更可能会发现本文的诸多错误,阅读本文只会让你更多地浪费生命,对此我十分抱歉。
好吧,节目继续。
第五部分是在目标程序可以运行的情况下,完善相关的接口,并进一步把之前移植造成的问题修复。
别忘了,之前我们写的接口还都是空函数,现在得动真格了。
好在程序已经可以下载并且调试了,之后的一切问题都可以通过调试来说话。
一切都从把示例程序的屏幕驱动移进来开始。尤其要注意包括示例的有关工程设置。
接下来呢,我们不能动态加载游戏ROM了,那么我就送给大家一个游戏ROM数组,加到工程里。具体内容各位到末尾的工程里获取吧。
当然了,读入ROM时也就不用文件名了,所以文件名参数pszFileName在本次移植之后,就没有用了,自己知道就好。
首先NesPalette是一个64个元素的调色盘数组,具体的颜色排布"翔"见下图。
参考win32工程里面的,发现源代码在此处有一定的奥秘,是关于前后景的,想一想马里奥大叔顶出问号中的蘑菇吧,是有遮挡关系的。
所以不得不承认,有一些地方需要调整,这里大家详见代码吧,我把这些解释为:NES只有64种颜色,原win32工程使用5:5:5表示RGB,我们更多时候可能要使用5:6:5,这样就用多余的一位来表示该颜色透明(即应该显示其后面被挡住的颜色),就这样吧,解释太多都是噩梦。
我的颜色表是:
- WORD NesPalette[64]={
- 0x738E,0x88C4,0xA800,0x9808,0x7011,0x1015,0x0014,0x004F,
- 0x0148,0x0200,0x0280,0x11C0,0x59C3,0x0000,0x0000,0x0000,
- 0xBDD7,0xEB80,0xE9C4,0xF010,0xB817,0x581C,0x015B,0x0A59,
- 0x0391,0x0480,0x0540,0x3C80,0x8C00,0x0000,0x0000,0x0000,
- 0xFFDF,0xFDC7,0xFC8B,0xFC48,0xFBDE,0xB39F,0x639F,0x3CDF,
- 0x3DDE,0x1690,0x4EC9,0x9FCB,0xDF40,0x0000,0x0000,0x0000,
- 0xFFDF,0xFF15,0xFE98,0xFE5A,0xFE1F,0xDE1F,0xB5DF,0xAEDF,
- 0xA71F,0xA7DC,0xBF95,0xCFD6,0xF7D3,0x0000,0x0000,0x0000,
- };
反正不是RGB,就是BGR啦,位数是5:6:5,如果觉得颜色有问题的话,自己调整一下R和B的顺序,G的6位只有高5位有效,低那一位表示是否透明,对于我们描画可以忽略这个值啦(最低位1或者0无所谓,不会影响这个颜色的整体颜色感觉)。
于是要在设置中配置透明标志:
- #define TRANSPARENT_COLOR 0x0020
然后是InfoNES_ReadRom函数,用来把rom各种信息加载给模拟器核心。
- /* Read ROM image file */
- extern const BYTE nes_rom[];
- int InfoNES_ReadRom( const char *pszFileName )
- {
- /*
- * Read ROM image file
- *
- * Parameters
- * const char *pszFileName (Read)
- *
- * Return values
- * 0 : Normally
- * -1 : Error
- */
- /* Read ROM Header */
- BYTE * rom = (BYTE*)nes_rom;
- memcpy( &NesHeader, rom, sizeof(NesHeader));
- if ( memcmp( NesHeader.byID, "NES\x1a", 4 ) != 0 )
- {
- /* not .nes file */
- return -1;
- }
- rom += sizeof(NesHeader);
- /* Clear SRAM */
- memset( SRAM, 0, SRAM_SIZE );
- /* If trainer presents Read Triner at 0x7000-0x71ff */
- if ( NesHeader.byInfo1 & 4 )
- {
- //memcpy( &SRAM[ 0x1000 ], rom, 512);
- rom += 512;
- }
- /* Allocate Memory for ROM Image */
- ROM = rom;
- rom += NesHeader.byRomSize * 0x4000;
- if ( NesHeader.byVRomSize > 0 )
- {
- /* Allocate Memory for VROM Image */
- VROM = (BYTE*)rom;
- rom += NesHeader.byVRomSize * 0x2000;
- }
- /* Successful */
- return 0;
- }
InfoNES_LoadLine函数用来将一行256个像素点画在屏幕上。
- /* Transfer the contents of work line on the screen */
- void InfoNES_LoadLine()
- {
- int i;
- for(i=0;i<256;i++)
- {
- GrContextForegroundSet(&sContext, WorkLine[i]);
- GrPixelDraw(&sContext, i, PPU_Scanline);
- }
- }
还有两个内存处理函数:
- /* memcpy */
- void *InfoNES_MemoryCopy( void *dest, const void *src, int count ){return memcpy(dest,src,count);}
- /* memset */
- void *InfoNES_MemorySet( void *dest, int c, int count ){return memset(dest,c,count);}
事实上我的工程还增加了提高描画效率的处理和时间的有关处理,但是你要知道,只要上面这些内容搞对了,模拟器就可以疯跑啦。
前一部分还留了个坑,有两个函数从内部屏蔽了代码:InfoNES_DrawLine和InfoNES_GetSprHitY,请改成如下的样子。
- /*===================================================================*/
- /* */
- /* InfoNES_DrawLine() : Render a scanline */
- /* */
- /*===================================================================*/
- void InfoNES_DrawLine()
- {
- /*
- * Render a scanline
- *
- */
- int nX;
- int nY;
- int nY4;
- int nYBit;
- WORD *pPalTbl;
- BYTE *pAttrBase;
- WORD *pPoint;
- int nNameTable;
- BYTE *pbyNameTable;
- BYTE *pbyChrData;
- #ifdef RAM_LACK
- BYTE tileData[2] = {0, 0};
- #else
- int nIdx;
- #endif /* RAM_LACK */
- BYTE *pSPRRAM;
- int nAttr;
- int nSprCnt;
- int nSprData;
- BYTE bySprCol;
- BYTE pSprBuf[ NES_DISP_WIDTH + 7 ];
- /*-------------------------------------------------------------------*/
- /* Render Background */
- /*-------------------------------------------------------------------*/
- /* MMC5 VROM switch */
- MapperRenderScreen( 1 );
- // Pointer to the render position
- #if WORKFRAME_DEFINE == WORKFRAME_NONE
- pPoint = WorkLine;
- #else
- pPoint = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
- #endif
- // Clear a scanline if screen is off
- if ( !( PPU_R1 & R1_SHOW_SCR ) )
- {
- InfoNES_MemorySet( pPoint, 0, NES_DISP_WIDTH << 1 );
- }
- else
- {
- nNameTable = PPU_NameTableBank;
- nY = PPU_Scr_V_Byte + ( PPU_Scanline >> 3 );
- nYBit = PPU_Scr_V_Bit + ( PPU_Scanline & 7 );
- if ( nYBit > 7 )
- {
- ++nY;
- nYBit &= 7;
- }
- #ifndef RAM_LACK
- nYBit <<= 3;
- #endif /* !RAM_LACK */
- if ( nY > 29 )
- {
- // Next NameTable (An up-down direction)
- nNameTable ^= NAME_TABLE_V_MASK;
- nY -= 30;
- }
- nX = PPU_Scr_H_Byte;
- nY4 = ( ( nY & 2 ) << 1 );
- /*-------------------------------------------------------------------*/
- /* Rendering of the block of the left end */
- /*-------------------------------------------------------------------*/
- pbyNameTable = PPUBANK[ nNameTable ] + nY * 32 + nX;
- #ifdef RAM_LACK
- pbyChrData = PPU_BG_Base + ( *pbyNameTable << 4 ) + nYBit;
- tileData[0] = ( ( pbyChrData[ 0 ] >> 1 ) & 0x55 ) | ( pbyChrData[ 8 ] & 0xAA );
- tileData[1] = ( pbyChrData[ 0 ] & 0x55 ) | ( ( pbyChrData[ 8 ] << 1 ) & 0xAA );
- #else
- pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
- #endif /* RAM_LACK */
- pAttrBase = PPUBANK[ nNameTable ] + 0x3c0 + ( nY / 4 ) * 8;
- pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
- #ifdef RAM_LACK
- switch(PPU_Scr_H_Bit)
- {
- case 0:
- *( pPoint++ ) = pPalTbl[ ( tileData[0] >> 6 ) & 3 ];
- case 1:
- *( pPoint++ ) = pPalTbl[ ( tileData[1] >> 6 ) & 3 ];
- case 2:
- *( pPoint++ ) = pPalTbl[ ( tileData[0] >> 4 ) & 3 ];
- case 3:
- *( pPoint++ ) = pPalTbl[ ( tileData[1] >> 4 ) & 3 ];
- case 4:
- *( pPoint++ ) = pPalTbl[ ( tileData[0] >> 2 ) & 3 ];
- case 5:
- *( pPoint++ ) = pPalTbl[ ( tileData[1] >> 2 ) & 3 ];
- case 6:
- *( pPoint++ ) = pPalTbl[ ( tileData[0] ) & 3 ];
- case 7:
- *( pPoint++ ) = pPalTbl[ ( tileData[1] ) & 3 ];
- default:
- break;
- }
- #else
- for ( nIdx = PPU_Scr_H_Bit; nIdx < 8; ++nIdx )
- {
- *( pPoint++ ) = pPalTbl[ pbyChrData[ nIdx ] ];
- }
- #endif
- // Callback at PPU read/write
- MapperPPU( PATTBL( pbyChrData ) );
- ++nX;
- ++pbyNameTable;
- /*-------------------------------------------------------------------*/
- /* Rendering of the left table */
- /*-------------------------------------------------------------------*/
- for ( ; nX < 32; ++nX )
- {
- #ifdef RAM_LACK
- pbyChrData = PPU_BG_Base + ( *pbyNameTable << 4 ) + nYBit;
- tileData[0] = ( ( pbyChrData[ 0 ] >> 1 ) & 0x55 ) | ( pbyChrData[ 8 ] & 0xAA );
- tileData[1] = ( pbyChrData[ 0 ] & 0x55 ) | ( ( pbyChrData[ 8 ] << 1 ) & 0xAA );
- #else
- pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
- #endif /* RAM_LACK */
- pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
- #ifdef RAM_LACK
- pPoint[ 0 ] = pPalTbl[ ( tileData[0] >> 6 ) & 3 ];
- pPoint[ 1 ] = pPalTbl[ ( tileData[1] >> 6 ) & 3 ];
- pPoint[ 2 ] = pPalTbl[ ( tileData[0] >> 4 ) & 3 ];
- pPoint[ 3 ] = pPalTbl[ ( tileData[1] >> 4 ) & 3 ];
- pPoint[ 4 ] = pPalTbl[ ( tileData[0] >> 2 ) & 3 ];
- pPoint[ 5 ] = pPalTbl[ ( tileData[1] >> 2 ) & 3 ];
- pPoint[ 6 ] = pPalTbl[ ( tileData[0] ) & 3 ];
- pPoint[ 7 ] = pPalTbl[ ( tileData[1] ) & 3 ];
- #else
- pPoint[ 0 ] = pPalTbl[ pbyChrData[ 0 ] ];
- pPoint[ 1 ] = pPalTbl[ pbyChrData[ 1 ] ];
- pPoint[ 2 ] = pPalTbl[ pbyChrData[ 2 ] ];
- pPoint[ 3 ] = pPalTbl[ pbyChrData[ 3 ] ];
- pPoint[ 4 ] = pPalTbl[ pbyChrData[ 4 ] ];
- pPoint[ 5 ] = pPalTbl[ pbyChrData[ 5 ] ];
- pPoint[ 6 ] = pPalTbl[ pbyChrData[ 6 ] ];
- pPoint[ 7 ] = pPalTbl[ pbyChrData[ 7 ] ];
- #endif
- pPoint += 8;
- // Callback at PPU read/write
- MapperPPU( PATTBL( pbyChrData ) );
- ++pbyNameTable;
- }
- // Holizontal Mirror
- nNameTable ^= NAME_TABLE_H_MASK;
- pbyNameTable = PPUBANK[ nNameTable ] + nY * 32;
- pAttrBase = PPUBANK[ nNameTable ] + 0x3c0 + ( nY / 4 ) * 8;
- /*-------------------------------------------------------------------*/
- /* Rendering of the right table */
- /*-------------------------------------------------------------------*/
- for ( nX = 0; nX < PPU_Scr_H_Byte; ++nX )
- {
- #ifdef RAM_LACK
- pbyChrData = PPU_BG_Base + ( *pbyNameTable << 4 ) + nYBit;
- tileData[0] = ( ( pbyChrData[ 0 ] >> 1 ) & 0x55 ) | ( pbyChrData[ 8 ] & 0xAA );
- tileData[1] = ( pbyChrData[ 0 ] & 0x55 ) | ( ( pbyChrData[ 8 ] << 1 ) & 0xAA );
- #else
- pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
- #endif /* RAM_LACK */
- pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
- #ifdef RAM_LACK
- pPoint[ 0 ] = pPalTbl[ ( tileData[0] >> 6 ) & 3 ];
- pPoint[ 1 ] = pPalTbl[ ( tileData[1] >> 6 ) & 3 ];
- pPoint[ 2 ] = pPalTbl[ ( tileData[0] >> 4 ) & 3 ];
- pPoint[ 3 ] = pPalTbl[ ( tileData[1] >> 4 ) & 3 ];
- pPoint[ 4 ] = pPalTbl[ ( tileData[0] >> 2 ) & 3 ];
- pPoint[ 5 ] = pPalTbl[ ( tileData[1] >> 2 ) & 3 ];
- pPoint[ 6 ] = pPalTbl[ ( tileData[0] ) & 3 ];
- pPoint[ 7 ] = pPalTbl[ ( tileData[1] ) & 3 ];
- #else
- pPoint[ 0 ] = pPalTbl[ pbyChrData[ 0 ] ];
- pPoint[ 1 ] = pPalTbl[ pbyChrData[ 1 ] ];
- pPoint[ 2 ] = pPalTbl[ pbyChrData[ 2 ] ];
- pPoint[ 3 ] = pPalTbl[ pbyChrData[ 3 ] ];
- pPoint[ 4 ] = pPalTbl[ pbyChrData[ 4 ] ];
- pPoint[ 5 ] = pPalTbl[ pbyChrData[ 5 ] ];
- pPoint[ 6 ] = pPalTbl[ pbyChrData[ 6 ] ];
- pPoint[ 7 ] = pPalTbl[ pbyChrData[ 7 ] ];
- #endif
- pPoint += 8;
- // Callback at PPU read/write
- MapperPPU( PATTBL( pbyChrData ) );
- ++pbyNameTable;
- }
- /*-------------------------------------------------------------------*/
- /* Rendering of the block of the right end */
- /*-------------------------------------------------------------------*/
- #ifdef RAM_LACK
- pbyChrData = PPU_BG_Base + ( *pbyNameTable << 4 ) + nYBit;
- tileData[0] = ( ( pbyChrData[ 0 ] >> 1 ) & 0x55 ) | ( pbyChrData[ 8 ] & 0xAA );
- tileData[1] = ( pbyChrData[ 0 ] & 0x55 ) | ( ( pbyChrData[ 8 ] << 1 ) & 0xAA );
- #else
- pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
- #endif /* RAM_LACK */
- pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
- #ifdef RAM_LACK
- switch(PPU_Scr_H_Bit)
- {
- case 8:
- pPoint[ 7 ] = pPalTbl[ ( tileData[1] ) & 3 ];
- case 7:
- pPoint[ 6 ] = pPalTbl[ ( tileData[0] ) & 3 ];
- case 6:
- pPoint[ 5 ] = pPalTbl[ ( tileData[1] >> 2 ) & 3 ];
- case 5:
- pPoint[ 4 ] = pPalTbl[ ( tileData[0] >> 2 ) & 3 ];
- case 4:
- pPoint[ 3 ] = pPalTbl[ ( tileData[1] >> 4 ) & 3 ];
- case 3:
- pPoint[ 2 ] = pPalTbl[ ( tileData[0] >> 4 ) & 3 ];
- case 2:
- pPoint[ 1 ] = pPalTbl[ ( tileData[1] >> 6 ) & 3 ];
- case 1:
- pPoint[ 0 ] = pPalTbl[ ( tileData[0] >> 6 ) & 3 ];
- case 0:
- default:
- break;
- }
- #else
- for ( nIdx = 0; nIdx < PPU_Scr_H_Bit; ++nIdx )
- {
- pPoint[ nIdx ] = pPalTbl[ pbyChrData[ nIdx ] ];
- }
- #endif
- // Callback at PPU read/write
- MapperPPU( PATTBL( pbyChrData ) );
- /*-------------------------------------------------------------------*/
- /* Backgroud Clipping */
- /*-------------------------------------------------------------------*/
- if ( !( PPU_R1 & R1_CLIP_BG ) )
- {
- WORD *pPointTop;
- #if WORKFRAME_DEFINE == WORKFRAME_NONE
- pPointTop = WorkLine;
- #else
- pPointTop = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
- #endif
- InfoNES_MemorySet( pPointTop, 0, 8 << 1 );
- }
- /*-------------------------------------------------------------------*/
- /* Clear a scanline if up and down clipping flag is set */
- /*-------------------------------------------------------------------*/
- if ( PPU_UpDown_Clip &&
- ( SCAN_ON_SCREEN_START > PPU_Scanline || PPU_Scanline > SCAN_BOTTOM_OFF_SCREEN_START ) )
- {
- WORD *pPointTop;
- #if WORKFRAME_DEFINE == WORKFRAME_NONE
- pPointTop = WorkLine;
- #else
- pPointTop = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
- #endif
- InfoNES_MemorySet( pPointTop, 0, NES_DISP_WIDTH << 1 );
- }
- }
- /*-------------------------------------------------------------------*/
- /* Render a sprite */
- /*-------------------------------------------------------------------*/
- /* MMC5 VROM switch */
- MapperRenderScreen( 0 );
- if ( PPU_R1 & R1_SHOW_SP )
- {
- // Reset Scanline Sprite Count
- PPU_R2 &= ~R2_MAX_SP;
- // Reset sprite buffer
- InfoNES_MemorySet( pSprBuf, 0, sizeof pSprBuf );
- // Render a sprite to the sprite buffer
- nSprCnt = 0;
- for ( pSPRRAM = SPRRAM + ( 63 << 2 ); pSPRRAM >= SPRRAM; pSPRRAM -= 4 )
- {
- nY = pSPRRAM[ SPR_Y ] + 1;
- if ( nY > PPU_Scanline || nY + PPU_SP_Height <= PPU_Scanline )
- continue; // Next sprite
- /*-------------------------------------------------------------------*/
- /* A sprite in scanning line */
- /*-------------------------------------------------------------------*/
- // Holizontal Sprite Count +1
- ++nSprCnt;
-
- nAttr = pSPRRAM[ SPR_ATTR ];
- nYBit = PPU_Scanline - nY;
- nYBit = ( nAttr & SPR_ATTR_V_FLIP ) ? ( PPU_SP_Height - nYBit - 1 ) : nYBit;
- #ifndef RAM_LACK
- nYBit <<= 3;
- #endif /* !RAM_LACK */
- if ( PPU_R0 & R0_SP_SIZE )
- {
- // Sprite size 8x16
- if ( pSPRRAM[ SPR_CHR ] & 1 )
- {
- #ifdef RAM_LACK
- pbyChrData = PPUBANK[ 4 ] + ( ( pSPRRAM[ SPR_CHR ] & 0xfe ) << 4 ) + ((nYBit & 0x8) << 1) + (nYBit & 0x7);
- #else
- pbyChrData = ChrBuf + 256 * 64 + ( ( pSPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit;
- #endif /* RAM_LACK */
- }
- else
- {
- #ifdef RAM_LACK
- pbyChrData = PPUBANK[ 0 ] + ( ( pSPRRAM[ SPR_CHR ] & 0xfe ) << 4 ) + ((nYBit & 0x8) << 1) + (nYBit & 0x7);
- #else
- pbyChrData = ChrBuf + ( ( pSPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit;
- #endif /* RAM_LACK */
- }
- }
- else
- {
- // Sprite size 8x8
- #ifdef RAM_LACK
- pbyChrData = PPU_SP_Base + ( pSPRRAM[ SPR_CHR ] << 4 ) + nYBit;
- #else
- pbyChrData = PPU_SP_Base + ( pSPRRAM[ SPR_CHR ] << 6 ) + nYBit;
- #endif /* RAM_LACK */
- }
- nAttr ^= SPR_ATTR_PRI;
- bySprCol = ( nAttr & ( SPR_ATTR_COLOR | SPR_ATTR_PRI ) ) << 2;
- nX = pSPRRAM[ SPR_X ];
- #ifdef RAM_LACK
- tileData[0] = ( ( pbyChrData[ 0 ] >> 1 ) & 0x55 ) | ( pbyChrData[ 8 ] & 0xAA );
- tileData[1] = ( pbyChrData[ 0 ] & 0x55 ) | ( ( pbyChrData[ 8 ] << 1 ) & 0xAA );
- #endif /* RAM_LACK */
- if ( nAttr & SPR_ATTR_H_FLIP )
- {
- // Horizontal flip
- #ifdef RAM_LACK
- if ( ( tileData[1] ) & 3 )
- pSprBuf[ nX ] = bySprCol | (( tileData[1] ) & 3);
- if ( ( tileData[0] ) & 3 )
- pSprBuf[ nX + 1 ] = bySprCol | (( tileData[0] ) & 3);
- if ( ( tileData[1] >> 2 ) & 3 )
- pSprBuf[ nX + 2 ] = bySprCol | (( tileData[1] >> 2 ) & 3);
- if ( ( tileData[0] >> 2 ) & 3 )
- pSprBuf[ nX + 3 ] = bySprCol | (( tileData[0] >> 2 ) & 3);
- if ( ( tileData[1] >> 4 ) & 3 )
- pSprBuf[ nX + 4 ] = bySprCol | (( tileData[1] >> 4 ) & 3);
- if ( ( tileData[0] >> 4 ) & 3 )
- pSprBuf[ nX + 5 ] = bySprCol | (( tileData[0] >> 4 ) & 3);
- if ( ( tileData[1] >> 6 ) & 3 )
- pSprBuf[ nX + 6 ] = bySprCol | (( tileData[1] >> 6 ) & 3);
- if ( ( tileData[0] >> 6 ) & 3 )
- pSprBuf[ nX + 7 ] = bySprCol | (( tileData[0] >> 6 ) & 3);
- #else
- if ( pbyChrData[ 7 ] )
- pSprBuf[ nX ] = bySprCol | pbyChrData[ 7 ];
- if ( pbyChrData[ 6 ] )
- pSprBuf[ nX + 1 ] = bySprCol | pbyChrData[ 6 ];
- if ( pbyChrData[ 5 ] )
- pSprBuf[ nX + 2 ] = bySprCol | pbyChrData[ 5 ];
- if ( pbyChrData[ 4 ] )
- pSprBuf[ nX + 3 ] = bySprCol | pbyChrData[ 4 ];
- if ( pbyChrData[ 3 ] )
- pSprBuf[ nX + 4 ] = bySprCol | pbyChrData[ 3 ];
- if ( pbyChrData[ 2 ] )
- pSprBuf[ nX + 5 ] = bySprCol | pbyChrData[ 2 ];
- if ( pbyChrData[ 1 ] )
- pSprBuf[ nX + 6 ] = bySprCol | pbyChrData[ 1 ];
- if ( pbyChrData[ 0 ] )
- pSprBuf[ nX + 7 ] = bySprCol | pbyChrData[ 0 ];
- #endif /* RAM_LACK */
- }
- else
- {
- // Non flip
- #ifdef RAM_LACK
- if ( ( tileData[0] >> 6 ) & 3 )
- pSprBuf[ nX ] = bySprCol | (( tileData[0] >> 6 ) & 3);
- if ( ( tileData[1] >> 6 ) & 3 )
- pSprBuf[ nX + 1 ] = bySprCol | (( tileData[1] >> 6 ) & 3);
- if ( ( tileData[0] >> 4 ) & 3 )
- pSprBuf[ nX + 2 ] = bySprCol | (( tileData[0] >> 4 ) & 3);
- if ( ( tileData[1] >> 4 ) & 3 )
- pSprBuf[ nX + 3 ] = bySprCol | (( tileData[1] >> 4 ) & 3);
- if ( ( tileData[0] >> 2 ) & 3 )
- pSprBuf[ nX + 4 ] = bySprCol | (( tileData[0] >> 2 ) & 3);
- if ( ( tileData[1] >> 2 ) & 3 )
- pSprBuf[ nX + 5 ] = bySprCol | (( tileData[1] >> 2 ) & 3);
- if ( ( tileData[0] ) & 3 )
- pSprBuf[ nX + 6 ] = bySprCol | (( tileData[0] ) & 3);
- if ( ( tileData[1] ) & 3 )
- pSprBuf[ nX + 7 ] = bySprCol | (( tileData[1] ) & 3);
- #else
- if ( pbyChrData[ 0 ] )
- pSprBuf[ nX ] = bySprCol | pbyChrData[ 0 ];
- if ( pbyChrData[ 1 ] )
- pSprBuf[ nX + 1 ] = bySprCol | pbyChrData[ 1 ];
- if ( pbyChrData[ 2 ] )
- pSprBuf[ nX + 2 ] = bySprCol | pbyChrData[ 2 ];
- if ( pbyChrData[ 3 ] )
- pSprBuf[ nX + 3 ] = bySprCol | pbyChrData[ 3 ];
- if ( pbyChrData[ 4 ] )
- pSprBuf[ nX + 4 ] = bySprCol | pbyChrData[ 4 ];
- if ( pbyChrData[ 5 ] )
- pSprBuf[ nX + 5 ] = bySprCol | pbyChrData[ 5 ];
- if ( pbyChrData[ 6 ] )
- pSprBuf[ nX + 6 ] = bySprCol | pbyChrData[ 6 ];
- if ( pbyChrData[ 7 ] )
- pSprBuf[ nX + 7 ] = bySprCol | pbyChrData[ 7 ];
- #endif /* RAM_LACK */
- }
- }
- // Rendering sprite
- pPoint -= ( NES_DISP_WIDTH - PPU_Scr_H_Bit );
- for ( nX = 0; nX < NES_DISP_WIDTH; ++nX )
- {
- nSprData = pSprBuf[ nX ];
- if ( nSprData && ( nSprData & 0x80 || pPoint[ nX ] & 0x8000 ) )
- {
- pPoint[ nX ] = PalTable[ ( nSprData & 0xf ) + 0x10 ];
- }
- }
- /*-------------------------------------------------------------------*/
- /* Sprite Clipping */
- /*-------------------------------------------------------------------*/
- if ( !( PPU_R1 & R1_CLIP_SP ) )
- {
- WORD *pPointTop;
- #if WORKFRAME_DEFINE == WORKFRAME_NONE
- pPointTop = WorkLine;
- #else
- pPointTop = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
- #endif
- InfoNES_MemorySet( pPointTop, 0, 8 << 1 );
- }
- if ( nSprCnt >= 8 )
- PPU_R2 |= R2_MAX_SP; // Set a flag of maximum sprites on scanline
- }
- }
- /*===================================================================*/
- /* */
- /* InfoNES_GetSprHitY() : Get a position of scanline hits sprite #0 */
- /* */
- /*===================================================================*/
- void InfoNES_GetSprHitY()
- {
- /*
- * Get a position of scanline hits sprite #0
- *
- */
- int nYBit;
- int nLine;
- #ifdef RAM_LACK
- BYTE *pbyChrData;
- #else
- DWORD *pdwChrData;
- #endif
- int nOff;
- if ( SPRRAM[ SPR_ATTR ] & SPR_ATTR_V_FLIP )
- {
- // Vertical flip
- nYBit = ( PPU_SP_Height - 1 );
- #ifdef RAM_LACK
- nOff = -1;
- #else
- nYBit <<= 3;
- nOff = -2;
- #endif /* RAM_LACK */
- }
- else
- {
- // Non flip
- nYBit = 0;
- #ifdef RAM_LACK
- nOff = 1;
- #else
- nOff = 2;
- #endif /* RAM_LACK */
- }
- if ( PPU_R0 & R0_SP_SIZE )
- {
- // Sprite size 8x16
- if ( SPRRAM[ SPR_CHR ] & 1 )
- {
- #ifdef RAM_LACK
- pbyChrData = ( PPUBANK[ 4 ] + ( ( SPRRAM[ SPR_CHR ] & 0xfe ) << 4 ) + ((nYBit & 0x8) << 1) + (nYBit & 0x7) );
- #else
- pdwChrData = (DWORD *)( ChrBuf + 256 * 64 + ( ( SPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit );
- #endif /* RAM_LACK */
- }
- else
- {
- #ifdef RAM_LACK
- pbyChrData = ( PPUBANK[ 0 ] + ( ( SPRRAM[ SPR_CHR ] & 0xfe ) << 4 ) + ((nYBit & 0x8) << 1) + (nYBit & 0x7) );
- #else
- pdwChrData = (DWORD * )( ChrBuf + ( ( SPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit );
- #endif /* RAM_LACK */
- }
- }
- else
- {
- // Sprite size 8x8
- #ifdef RAM_LACK
- pbyChrData = ( PPU_SP_Base + ( SPRRAM[ SPR_CHR ] << 4 ) + nYBit );
- #else
- pdwChrData = (DWORD *)( PPU_SP_Base + ( SPRRAM[ SPR_CHR ] << 6 ) + nYBit );
- #endif /* RAM_LACK */
- }
- if ( ( SPRRAM[ SPR_Y ] + 1 <= SCAN_UNKNOWN_START ) && ( SPRRAM[SPR_Y] > 0 ) )
- {
- for ( nLine = 0; nLine < PPU_SP_Height; nLine++ )
- {
- #ifdef RAM_LACK
- if ( pbyChrData[ 0 ] | pbyChrData[ 8 ] )
- #else
- if ( pdwChrData[ 0 ] | pdwChrData[ 1 ] )
- #endif /* RAM_LACK */
- {
- // Scanline hits sprite #0
- SpriteJustHit = SPRRAM[SPR_Y] + 1 + nLine;
- nLine = SCAN_VBLANK_END;
- }
- #ifdef RAM_LACK
- if( nLine == 7)
- {
- pbyChrData += nOff * 9;
- }
- else
- {
- pbyChrData += nOff;
- }
- #else
- pdwChrData += nOff;
- #endif /* RAM_LACK */
- }
- } else {
- // Scanline didn't hit sprite #0
- SpriteJustHit = SCAN_UNKNOWN_START + 1;
- }
- }
需要解释的是,我也是参照之前原有的处理调整成这个样的,也许不免会有Bug,比如我刚刚还修改了一处不细心导致的问题。
与此同时,有一处临时修改现在需要更正,那就是InfoNES_Mapper.h的60行,修改成:
- #define PATTBL(a) ( (a) - PPUBANK[ 0 ] )
基本上有了这些,模拟器就算是完工咯。
以上就是这次移植的主要工作了。
最后不要忘记调整堆栈的大小,我就是忘了调整大小了,结果马里奥大叔都穿墙跑啦……而且还不容易发现错误在哪。
什么?有童鞋说跑得很慢?第一,请开启编译优化,建议试试速度优化O3;第二,InfoNES里有个叫FrameSkip的变量,嘿嘿……
对了,似乎这样还不能控制哪,怎么处理手柄消息呢?还记得接口函数中有个:
- /* Get a joypad state */
- void InfoNES_PadState( DWORD *pdwPad1, DWORD
- *pdwPad2, DWORD *pdwSystem )
- {
- }
在这里给pdwPad1和pdwPad2赋值就OK了。
具体怎么赋值?可能可以参考下图吧,我不再继续研究啦。
最后是LM4F232板上可以运行的最终工程(不能控制):
另外,这InfoNES,据我仔细观察,也不是百试百灵的,好像沙罗曼蛇玩着就非常不正常。
不过能在我的80MHz的小板上跑马里奥大叔,我也是非常欣慰了。