[原创] 【试用心得】外传——《手把手教你移植InfoNES(到HANKER-LM4F232)》更新至20131030

sjtitr   2013-10-30 22:17 楼主
完结篇……更新至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个元素的调色盘数组,具体的颜色排布"翔"见下图。
color.jpg

参考win32工程里面的,发现源代码在此处有一定的奥秘,是关于前后景的,想一想马里奥大叔顶出问号中的蘑菇吧,是有遮挡关系的。

所以不得不承认,有一些地方需要调整,这里大家详见代码吧,我把这些解释为:NES只有64种颜色,原win32工程使用5:5:5表示RGB,我们更多时候可能要使用5:6:5,这样就用多余的一位来表示该颜色透明(即应该显示其后面被挡住的颜色),就这样吧,解释太多都是噩梦。
我的颜色表是:
  1. WORD NesPalette[64]={
  2.   0x738E,0x88C4,0xA800,0x9808,0x7011,0x1015,0x0014,0x004F,
  3.   0x0148,0x0200,0x0280,0x11C0,0x59C3,0x0000,0x0000,0x0000,
  4.   0xBDD7,0xEB80,0xE9C4,0xF010,0xB817,0x581C,0x015B,0x0A59,
  5.   0x0391,0x0480,0x0540,0x3C80,0x8C00,0x0000,0x0000,0x0000,
  6.   0xFFDF,0xFDC7,0xFC8B,0xFC48,0xFBDE,0xB39F,0x639F,0x3CDF,
  7.   0x3DDE,0x1690,0x4EC9,0x9FCB,0xDF40,0x0000,0x0000,0x0000,
  8.   0xFFDF,0xFF15,0xFE98,0xFE5A,0xFE1F,0xDE1F,0xB5DF,0xAEDF,
  9.   0xA71F,0xA7DC,0xBF95,0xCFD6,0xF7D3,0x0000,0x0000,0x0000,
  10. };
反正不是RGB,就是BGR啦,位数是5:6:5,如果觉得颜色有问题的话,自己调整一下R和B的顺序,G的6位只有高5位有效,低那一位表示是否透明,对于我们描画可以忽略这个值啦(最低位1或者0无所谓,不会影响这个颜色的整体颜色感觉)。
于是要在设置中配置透明标志:
  1. #define TRANSPARENT_COLOR 0x0020
然后是InfoNES_ReadRom函数,用来把rom各种信息加载给模拟器核心。
  1. /* Read ROM image file */
  2. extern const BYTE nes_rom[];
  3. int InfoNES_ReadRom( const char *pszFileName )
  4. {
  5. /*
  6. *  Read ROM image file
  7. *
  8. *  Parameters
  9. *    const char *pszFileName          (Read)
  10. *
  11. *  Return values
  12. *     0 : Normally
  13. *    -1 : Error
  14. */

  15.   /* Read ROM Header */
  16.   BYTE * rom = (BYTE*)nes_rom;
  17.   memcpy( &NesHeader, rom, sizeof(NesHeader));
  18.   if ( memcmp( NesHeader.byID, "NES\x1a", 4 ) != 0 )
  19.   {
  20.     /* not .nes file */
  21.     return -1;
  22.   }
  23.   rom += sizeof(NesHeader);
  24.   /* Clear SRAM */
  25.   memset( SRAM, 0, SRAM_SIZE );
  26.   /* If trainer presents Read Triner at 0x7000-0x71ff */
  27.   if ( NesHeader.byInfo1 & 4 )
  28.   {
  29.     //memcpy( &SRAM[ 0x1000 ], rom, 512);
  30. rom += 512;
  31.   }
  32.   /* Allocate Memory for ROM Image */
  33.   ROM = rom;
  34.   rom += NesHeader.byRomSize * 0x4000;
  35.   if ( NesHeader.byVRomSize > 0 )
  36.   {
  37.     /* Allocate Memory for VROM Image */
  38. VROM = (BYTE*)rom;
  39. rom += NesHeader.byVRomSize * 0x2000;
  40.   }
  41.   /* Successful */
  42.   return 0;
  43. }
InfoNES_LoadLine函数用来将一行256个像素点画在屏幕上。
  1. /* Transfer the contents of work line on the screen */
  2. void InfoNES_LoadLine()
  3. {
  4.   int i;
  5.   for(i=0;i<256;i++)
  6.   {
  7.     GrContextForegroundSet(&sContext, WorkLine[i]);
  8.   GrPixelDraw(&sContext, i, PPU_Scanline);
  9.   }
  10. }
还有两个内存处理函数:
  1. /* memcpy */
  2. void *InfoNES_MemoryCopy( void *dest, const void *src, int count ){return memcpy(dest,src,count);}
  3. /* memset */
  4. void *InfoNES_MemorySet( void *dest, int c, int count ){return memset(dest,c,count);}
事实上我的工程还增加了提高描画效率的处理和时间的有关处理,但是你要知道,只要上面这些内容搞对了,模拟器就可以疯跑啦。

前一部分还留了个坑,有两个函数从内部屏蔽了代码:InfoNES_DrawLine和InfoNES_GetSprHitY,请改成如下的样子。
  1. /*===================================================================*/
  2. /*                                                                   */
  3. /*              InfoNES_DrawLine() : Render a scanline               */
  4. /*                                                                   */
  5. /*===================================================================*/
  6. void InfoNES_DrawLine()
  7. {
  8. /*
  9. *  Render a scanline
  10. *
  11. */
  12.   int nX;
  13.   int nY;
  14.   int nY4;
  15.   int nYBit;
  16.   WORD *pPalTbl;
  17.   BYTE *pAttrBase;
  18.   WORD *pPoint;
  19.   int nNameTable;
  20.   BYTE *pbyNameTable;
  21.   BYTE *pbyChrData;
  22. #ifdef RAM_LACK
  23.   BYTE tileData[2] = {0, 0};
  24. #else
  25.   int nIdx;
  26. #endif /* RAM_LACK */
  27.   BYTE *pSPRRAM;
  28.   int nAttr;
  29.   int nSprCnt;
  30.   int nSprData;
  31.   BYTE bySprCol;
  32.   BYTE pSprBuf[ NES_DISP_WIDTH + 7 ];
  33.   /*-------------------------------------------------------------------*/
  34.   /*  Render Background                                                */
  35.   /*-------------------------------------------------------------------*/
  36.   /* MMC5 VROM switch */
  37.   MapperRenderScreen( 1 );
  38.   // Pointer to the render position
  39. #if WORKFRAME_DEFINE == WORKFRAME_NONE
  40.   pPoint = WorkLine;
  41. #else
  42.   pPoint = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
  43. #endif
  44.   // Clear a scanline if screen is off
  45.   if ( !( PPU_R1 & R1_SHOW_SCR ) )
  46.   {
  47.     InfoNES_MemorySet( pPoint, 0, NES_DISP_WIDTH << 1 );
  48.   }
  49.   else
  50.   {
  51.     nNameTable = PPU_NameTableBank;
  52.     nY = PPU_Scr_V_Byte + ( PPU_Scanline >> 3 );
  53.     nYBit = PPU_Scr_V_Bit + ( PPU_Scanline & 7 );
  54.     if ( nYBit > 7 )
  55.     {
  56.       ++nY;
  57.       nYBit &= 7;
  58.     }
  59. #ifndef RAM_LACK
  60.     nYBit <<= 3;
  61. #endif /* !RAM_LACK */
  62.     if ( nY > 29 )
  63.     {
  64.       // Next NameTable (An up-down direction)
  65.       nNameTable ^= NAME_TABLE_V_MASK;
  66.       nY -= 30;
  67.     }
  68.     nX = PPU_Scr_H_Byte;
  69.     nY4 = ( ( nY & 2 ) << 1 );
  70.     /*-------------------------------------------------------------------*/
  71.     /*  Rendering of the block of the left end                           */
  72.     /*-------------------------------------------------------------------*/
  73.     pbyNameTable = PPUBANK[ nNameTable ] + nY * 32 + nX;
  74. #ifdef RAM_LACK
  75.     pbyChrData = PPU_BG_Base + ( *pbyNameTable << 4 ) + nYBit;
  76.     tileData[0] = ( ( pbyChrData[ 0 ] >> 1 ) & 0x55 ) | ( pbyChrData[ 8 ] & 0xAA );
  77.     tileData[1] = ( pbyChrData[ 0 ] & 0x55 ) | ( ( pbyChrData[ 8 ] << 1 ) & 0xAA );
  78. #else
  79.     pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
  80. #endif /* RAM_LACK */
  81.     pAttrBase = PPUBANK[ nNameTable ] + 0x3c0 + ( nY / 4 ) * 8;
  82.     pPalTbl =  &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
  83. #ifdef RAM_LACK
  84. switch(PPU_Scr_H_Bit)
  85. {
  86.   case 0:
  87.     *( pPoint++ ) = pPalTbl[ ( tileData[0] >> 6 ) & 3 ];
  88.   case 1:
  89.     *( pPoint++ ) = pPalTbl[ ( tileData[1] >> 6 ) & 3 ];
  90.   case 2:
  91.     *( pPoint++ ) = pPalTbl[ ( tileData[0] >> 4 ) & 3 ];
  92.   case 3:
  93.     *( pPoint++ ) = pPalTbl[ ( tileData[1] >> 4 ) & 3 ];
  94.   case 4:
  95.     *( pPoint++ ) = pPalTbl[ ( tileData[0] >> 2 ) & 3 ];
  96.   case 5:
  97.     *( pPoint++ ) = pPalTbl[ ( tileData[1] >> 2 ) & 3 ];
  98.   case 6:
  99.     *( pPoint++ ) = pPalTbl[ ( tileData[0] ) & 3 ];
  100.   case 7:
  101.     *( pPoint++ ) = pPalTbl[ ( tileData[1] ) & 3 ];
  102.   default:
  103.    break;
  104. }
  105. #else
  106.     for ( nIdx = PPU_Scr_H_Bit; nIdx < 8; ++nIdx )
  107.     {
  108.       *( pPoint++ ) = pPalTbl[ pbyChrData[ nIdx ] ];
  109.     }
  110. #endif
  111.     // Callback at PPU read/write
  112.     MapperPPU( PATTBL( pbyChrData ) );
  113.     ++nX;
  114.     ++pbyNameTable;
  115.     /*-------------------------------------------------------------------*/
  116.     /*  Rendering of the left table                                      */
  117.     /*-------------------------------------------------------------------*/
  118.     for ( ; nX < 32; ++nX )
  119.     {
  120. #ifdef RAM_LACK
  121.       pbyChrData = PPU_BG_Base + ( *pbyNameTable << 4 ) + nYBit;
  122.       tileData[0] = ( ( pbyChrData[ 0 ] >> 1 ) & 0x55 ) | ( pbyChrData[ 8 ] & 0xAA );
  123.       tileData[1] = ( pbyChrData[ 0 ] & 0x55 ) | ( ( pbyChrData[ 8 ] << 1 ) & 0xAA );
  124. #else
  125.       pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
  126. #endif /* RAM_LACK */
  127.       pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
  128. #ifdef RAM_LACK
  129.       pPoint[ 0 ] = pPalTbl[ ( tileData[0] >> 6 ) & 3 ];
  130.       pPoint[ 1 ] = pPalTbl[ ( tileData[1] >> 6 ) & 3 ];
  131.       pPoint[ 2 ] = pPalTbl[ ( tileData[0] >> 4 ) & 3 ];
  132.       pPoint[ 3 ] = pPalTbl[ ( tileData[1] >> 4 ) & 3 ];
  133.       pPoint[ 4 ] = pPalTbl[ ( tileData[0] >> 2 ) & 3 ];
  134.       pPoint[ 5 ] = pPalTbl[ ( tileData[1] >> 2 ) & 3 ];
  135.       pPoint[ 6 ] = pPalTbl[ ( tileData[0] ) & 3 ];
  136.       pPoint[ 7 ] = pPalTbl[ ( tileData[1] ) & 3 ];
  137. #else
  138.       pPoint[ 0 ] = pPalTbl[ pbyChrData[ 0 ] ];
  139.       pPoint[ 1 ] = pPalTbl[ pbyChrData[ 1 ] ];
  140.       pPoint[ 2 ] = pPalTbl[ pbyChrData[ 2 ] ];
  141.       pPoint[ 3 ] = pPalTbl[ pbyChrData[ 3 ] ];
  142.       pPoint[ 4 ] = pPalTbl[ pbyChrData[ 4 ] ];
  143.       pPoint[ 5 ] = pPalTbl[ pbyChrData[ 5 ] ];
  144.       pPoint[ 6 ] = pPalTbl[ pbyChrData[ 6 ] ];
  145.       pPoint[ 7 ] = pPalTbl[ pbyChrData[ 7 ] ];
  146. #endif
  147.       pPoint += 8;
  148.       // Callback at PPU read/write
  149.       MapperPPU( PATTBL( pbyChrData ) );
  150.       ++pbyNameTable;
  151.     }
  152.     // Holizontal Mirror
  153.     nNameTable ^= NAME_TABLE_H_MASK;
  154.     pbyNameTable = PPUBANK[ nNameTable ] + nY * 32;
  155.     pAttrBase = PPUBANK[ nNameTable ] + 0x3c0 + ( nY / 4 ) * 8;
  156.     /*-------------------------------------------------------------------*/
  157.     /*  Rendering of the right table                                     */
  158.     /*-------------------------------------------------------------------*/
  159.     for ( nX = 0; nX < PPU_Scr_H_Byte; ++nX )
  160.     {
  161. #ifdef RAM_LACK
  162.       pbyChrData = PPU_BG_Base + ( *pbyNameTable << 4 ) + nYBit;
  163.       tileData[0] = ( ( pbyChrData[ 0 ] >> 1 ) & 0x55 ) | ( pbyChrData[ 8 ] & 0xAA );
  164.       tileData[1] = ( pbyChrData[ 0 ] & 0x55 ) | ( ( pbyChrData[ 8 ] << 1 ) & 0xAA );
  165. #else
  166.       pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
  167. #endif /* RAM_LACK */
  168.       pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
  169. #ifdef RAM_LACK
  170.       pPoint[ 0 ] = pPalTbl[ ( tileData[0] >> 6 ) & 3 ];
  171.       pPoint[ 1 ] = pPalTbl[ ( tileData[1] >> 6 ) & 3 ];
  172.       pPoint[ 2 ] = pPalTbl[ ( tileData[0] >> 4 ) & 3 ];
  173.       pPoint[ 3 ] = pPalTbl[ ( tileData[1] >> 4 ) & 3 ];
  174.       pPoint[ 4 ] = pPalTbl[ ( tileData[0] >> 2 ) & 3 ];
  175.       pPoint[ 5 ] = pPalTbl[ ( tileData[1] >> 2 ) & 3 ];
  176.       pPoint[ 6 ] = pPalTbl[ ( tileData[0] ) & 3 ];
  177.       pPoint[ 7 ] = pPalTbl[ ( tileData[1] ) & 3 ];
  178. #else
  179.       pPoint[ 0 ] = pPalTbl[ pbyChrData[ 0 ] ];
  180.       pPoint[ 1 ] = pPalTbl[ pbyChrData[ 1 ] ];
  181.       pPoint[ 2 ] = pPalTbl[ pbyChrData[ 2 ] ];
  182.       pPoint[ 3 ] = pPalTbl[ pbyChrData[ 3 ] ];
  183.       pPoint[ 4 ] = pPalTbl[ pbyChrData[ 4 ] ];
  184.       pPoint[ 5 ] = pPalTbl[ pbyChrData[ 5 ] ];
  185.       pPoint[ 6 ] = pPalTbl[ pbyChrData[ 6 ] ];
  186.       pPoint[ 7 ] = pPalTbl[ pbyChrData[ 7 ] ];
  187. #endif
  188.       pPoint += 8;
  189.       // Callback at PPU read/write
  190.       MapperPPU( PATTBL( pbyChrData ) );
  191.       ++pbyNameTable;
  192.     }
  193.     /*-------------------------------------------------------------------*/
  194.     /*  Rendering of the block of the right end                          */
  195.     /*-------------------------------------------------------------------*/
  196. #ifdef RAM_LACK
  197.     pbyChrData = PPU_BG_Base + ( *pbyNameTable << 4 ) + nYBit;
  198.     tileData[0] = ( ( pbyChrData[ 0 ] >> 1 ) & 0x55 ) | ( pbyChrData[ 8 ] & 0xAA );
  199.     tileData[1] = ( pbyChrData[ 0 ] & 0x55 ) | ( ( pbyChrData[ 8 ] << 1 ) & 0xAA );
  200. #else
  201.     pbyChrData = PPU_BG_Base + ( *pbyNameTable << 6 ) + nYBit;
  202. #endif /* RAM_LACK */
  203.     pPalTbl = &PalTable[ ( ( ( pAttrBase[ nX >> 2 ] >> ( ( nX & 2 ) + nY4 ) ) & 3 ) << 2 ) ];
  204. #ifdef RAM_LACK
  205. switch(PPU_Scr_H_Bit)
  206. {
  207.     case 8:
  208.     pPoint[ 7 ] = pPalTbl[ ( tileData[1] ) & 3 ];
  209.   case 7:
  210.     pPoint[ 6 ] = pPalTbl[ ( tileData[0] ) & 3 ];
  211.   case 6:
  212.     pPoint[ 5 ] = pPalTbl[ ( tileData[1] >> 2 ) & 3 ];
  213.   case 5:
  214.     pPoint[ 4 ] = pPalTbl[ ( tileData[0] >> 2 ) & 3 ];
  215.   case 4:
  216.     pPoint[ 3 ] = pPalTbl[ ( tileData[1] >> 4 ) & 3 ];
  217.   case 3:
  218.     pPoint[ 2 ] = pPalTbl[ ( tileData[0] >> 4 ) & 3 ];
  219.   case 2:
  220.     pPoint[ 1 ] = pPalTbl[ ( tileData[1] >> 6 ) & 3 ];
  221.   case 1:
  222.     pPoint[ 0 ] = pPalTbl[ ( tileData[0] >> 6 ) & 3 ];
  223.   case 0:
  224.   default:
  225.    break;
  226. }
  227. #else
  228.     for ( nIdx = 0; nIdx < PPU_Scr_H_Bit; ++nIdx )
  229.     {
  230.       pPoint[ nIdx ] = pPalTbl[ pbyChrData[ nIdx ] ];
  231.     }
  232. #endif
  233.     // Callback at PPU read/write
  234.     MapperPPU( PATTBL( pbyChrData ) );
  235.     /*-------------------------------------------------------------------*/
  236.     /*  Backgroud Clipping                                               */
  237.     /*-------------------------------------------------------------------*/
  238.     if ( !( PPU_R1 & R1_CLIP_BG ) )
  239.     {
  240.       WORD *pPointTop;
  241. #if WORKFRAME_DEFINE == WORKFRAME_NONE
  242.    pPointTop = WorkLine;
  243. #else
  244.    pPointTop = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
  245. #endif
  246.       InfoNES_MemorySet( pPointTop, 0, 8 << 1 );
  247.     }
  248.     /*-------------------------------------------------------------------*/
  249.     /*  Clear a scanline if up and down clipping flag is set             */
  250.     /*-------------------------------------------------------------------*/
  251.     if ( PPU_UpDown_Clip &&
  252.        ( SCAN_ON_SCREEN_START > PPU_Scanline || PPU_Scanline > SCAN_BOTTOM_OFF_SCREEN_START ) )
  253.     {
  254.       WORD *pPointTop;
  255. #if WORKFRAME_DEFINE == WORKFRAME_NONE
  256.    pPointTop = WorkLine;
  257. #else
  258.    pPointTop = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
  259. #endif
  260.       InfoNES_MemorySet( pPointTop, 0, NES_DISP_WIDTH << 1 );
  261.     }  
  262.   }
  263.   /*-------------------------------------------------------------------*/
  264.   /*  Render a sprite                                                  */
  265.   /*-------------------------------------------------------------------*/
  266.   /* MMC5 VROM switch */
  267.   MapperRenderScreen( 0 );
  268.   if ( PPU_R1 & R1_SHOW_SP )
  269.   {
  270.     // Reset Scanline Sprite Count
  271.     PPU_R2 &= ~R2_MAX_SP;
  272.     // Reset sprite buffer
  273.     InfoNES_MemorySet( pSprBuf, 0, sizeof pSprBuf );
  274.     // Render a sprite to the sprite buffer
  275.     nSprCnt = 0;
  276.     for ( pSPRRAM = SPRRAM + ( 63 << 2 ); pSPRRAM >= SPRRAM; pSPRRAM -= 4 )
  277.     {
  278.       nY = pSPRRAM[ SPR_Y ] + 1;
  279.       if ( nY > PPU_Scanline || nY + PPU_SP_Height <= PPU_Scanline )
  280.         continue;  // Next sprite
  281.      /*-------------------------------------------------------------------*/
  282.      /*  A sprite in scanning line                                        */
  283.      /*-------------------------------------------------------------------*/
  284.       // Holizontal Sprite Count +1
  285.       ++nSprCnt;
  286.       
  287.       nAttr = pSPRRAM[ SPR_ATTR ];
  288.       nYBit = PPU_Scanline - nY;
  289.       nYBit = ( nAttr & SPR_ATTR_V_FLIP ) ? ( PPU_SP_Height - nYBit - 1 ) : nYBit;
  290. #ifndef RAM_LACK
  291.       nYBit <<= 3;
  292. #endif /* !RAM_LACK */
  293.       if ( PPU_R0 & R0_SP_SIZE )
  294.       {
  295.         // Sprite size 8x16
  296.         if ( pSPRRAM[ SPR_CHR ] & 1 )
  297.         {
  298. #ifdef RAM_LACK
  299.           pbyChrData = PPUBANK[ 4 ] + ( ( pSPRRAM[ SPR_CHR ] & 0xfe ) << 4 ) + ((nYBit & 0x8) << 1) + (nYBit & 0x7);
  300. #else
  301.           pbyChrData = ChrBuf + 256 * 64 + ( ( pSPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit;
  302. #endif /* RAM_LACK */
  303.         }
  304.         else
  305.         {
  306. #ifdef RAM_LACK
  307.           pbyChrData = PPUBANK[ 0 ] + ( ( pSPRRAM[ SPR_CHR ] & 0xfe ) << 4 ) + ((nYBit & 0x8) << 1) + (nYBit & 0x7);
  308. #else
  309.           pbyChrData = ChrBuf + ( ( pSPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit;
  310. #endif /* RAM_LACK */
  311.         }
  312.       }
  313.       else
  314.       {
  315.         // Sprite size 8x8
  316. #ifdef RAM_LACK
  317.         pbyChrData = PPU_SP_Base + ( pSPRRAM[ SPR_CHR ] << 4 ) + nYBit;
  318. #else
  319.         pbyChrData = PPU_SP_Base + ( pSPRRAM[ SPR_CHR ] << 6 ) + nYBit;
  320. #endif /* RAM_LACK */
  321.       }
  322.       nAttr ^= SPR_ATTR_PRI;
  323.       bySprCol = ( nAttr & ( SPR_ATTR_COLOR | SPR_ATTR_PRI ) ) << 2;
  324.       nX = pSPRRAM[ SPR_X ];
  325. #ifdef RAM_LACK
  326.       tileData[0] = ( ( pbyChrData[ 0 ] >> 1 ) & 0x55 ) | ( pbyChrData[ 8 ] & 0xAA );
  327.       tileData[1] = ( pbyChrData[ 0 ] & 0x55 ) | ( ( pbyChrData[ 8 ] << 1 ) & 0xAA );
  328. #endif /* RAM_LACK */
  329.       if ( nAttr & SPR_ATTR_H_FLIP )
  330.       {
  331.         // Horizontal flip
  332. #ifdef RAM_LACK
  333.         if ( ( tileData[1] ) & 3 )
  334.           pSprBuf[ nX ]     = bySprCol | (( tileData[1] ) & 3);
  335.         if ( ( tileData[0] ) & 3 )
  336.           pSprBuf[ nX + 1 ] = bySprCol | (( tileData[0] ) & 3);
  337.         if ( ( tileData[1] >> 2 ) & 3 )
  338.           pSprBuf[ nX + 2 ] = bySprCol | (( tileData[1] >> 2 ) & 3);
  339.         if ( ( tileData[0] >> 2 ) & 3 )
  340.           pSprBuf[ nX + 3 ] = bySprCol | (( tileData[0] >> 2 ) & 3);
  341.         if ( ( tileData[1] >> 4 ) & 3 )
  342.           pSprBuf[ nX + 4 ] = bySprCol | (( tileData[1] >> 4 ) & 3);
  343.         if ( ( tileData[0] >> 4 ) & 3 )
  344.           pSprBuf[ nX + 5 ] = bySprCol | (( tileData[0] >> 4 ) & 3);
  345.         if ( ( tileData[1] >> 6 ) & 3 )
  346.           pSprBuf[ nX + 6 ] = bySprCol | (( tileData[1] >> 6 ) & 3);
  347.         if ( ( tileData[0] >> 6 ) & 3 )
  348.           pSprBuf[ nX + 7 ] = bySprCol | (( tileData[0] >> 6 ) & 3);
  349. #else
  350.         if ( pbyChrData[ 7 ] )
  351.           pSprBuf[ nX ]     = bySprCol | pbyChrData[ 7 ];
  352.         if ( pbyChrData[ 6 ] )
  353.           pSprBuf[ nX + 1 ] = bySprCol | pbyChrData[ 6 ];
  354.         if ( pbyChrData[ 5 ] )
  355.           pSprBuf[ nX + 2 ] = bySprCol | pbyChrData[ 5 ];
  356.         if ( pbyChrData[ 4 ] )
  357.           pSprBuf[ nX + 3 ] = bySprCol | pbyChrData[ 4 ];
  358.         if ( pbyChrData[ 3 ] )
  359.           pSprBuf[ nX + 4 ] = bySprCol | pbyChrData[ 3 ];
  360.         if ( pbyChrData[ 2 ] )
  361.           pSprBuf[ nX + 5 ] = bySprCol | pbyChrData[ 2 ];
  362.         if ( pbyChrData[ 1 ] )
  363.           pSprBuf[ nX + 6 ] = bySprCol | pbyChrData[ 1 ];
  364.         if ( pbyChrData[ 0 ] )
  365.           pSprBuf[ nX + 7 ] = bySprCol | pbyChrData[ 0 ];
  366. #endif /* RAM_LACK */
  367.       }
  368.       else
  369.       {
  370.         // Non flip
  371. #ifdef RAM_LACK
  372.         if ( ( tileData[0] >> 6 ) & 3 )
  373.           pSprBuf[ nX ]     = bySprCol | (( tileData[0] >> 6 ) & 3);
  374.         if ( ( tileData[1] >> 6 ) & 3 )
  375.           pSprBuf[ nX + 1 ] = bySprCol | (( tileData[1] >> 6 ) & 3);
  376.         if ( ( tileData[0] >> 4 ) & 3 )
  377.           pSprBuf[ nX + 2 ] = bySprCol | (( tileData[0] >> 4 ) & 3);
  378.         if ( ( tileData[1] >> 4 ) & 3 )
  379.           pSprBuf[ nX + 3 ] = bySprCol | (( tileData[1] >> 4 ) & 3);
  380.         if ( ( tileData[0] >> 2 ) & 3 )
  381.           pSprBuf[ nX + 4 ] = bySprCol | (( tileData[0] >> 2 ) & 3);
  382.         if ( ( tileData[1] >> 2 ) & 3 )
  383.           pSprBuf[ nX + 5 ] = bySprCol | (( tileData[1] >> 2 ) & 3);
  384.         if ( ( tileData[0] ) & 3 )
  385.           pSprBuf[ nX + 6 ] = bySprCol | (( tileData[0] ) & 3);
  386.         if ( ( tileData[1] ) & 3 )
  387.           pSprBuf[ nX + 7 ] = bySprCol | (( tileData[1] ) & 3);
  388. #else
  389.         if ( pbyChrData[ 0 ] )
  390.           pSprBuf[ nX ]     = bySprCol | pbyChrData[ 0 ];
  391.         if ( pbyChrData[ 1 ] )
  392.           pSprBuf[ nX + 1 ] = bySprCol | pbyChrData[ 1 ];
  393.         if ( pbyChrData[ 2 ] )
  394.           pSprBuf[ nX + 2 ] = bySprCol | pbyChrData[ 2 ];
  395.         if ( pbyChrData[ 3 ] )
  396.           pSprBuf[ nX + 3 ] = bySprCol | pbyChrData[ 3 ];
  397.         if ( pbyChrData[ 4 ] )
  398.           pSprBuf[ nX + 4 ] = bySprCol | pbyChrData[ 4 ];
  399.         if ( pbyChrData[ 5 ] )
  400.           pSprBuf[ nX + 5 ] = bySprCol | pbyChrData[ 5 ];
  401.         if ( pbyChrData[ 6 ] )
  402.           pSprBuf[ nX + 6 ] = bySprCol | pbyChrData[ 6 ];
  403.         if ( pbyChrData[ 7 ] )
  404.           pSprBuf[ nX + 7 ] = bySprCol | pbyChrData[ 7 ];
  405. #endif /* RAM_LACK */
  406.       }
  407.     }
  408.     // Rendering sprite
  409.     pPoint -= ( NES_DISP_WIDTH - PPU_Scr_H_Bit );
  410.     for ( nX = 0; nX < NES_DISP_WIDTH; ++nX )
  411.     {
  412.       nSprData = pSprBuf[ nX ];
  413.       if ( nSprData  && ( nSprData & 0x80 || pPoint[ nX ] & 0x8000 ) )
  414.       {
  415.         pPoint[ nX ] = PalTable[ ( nSprData & 0xf ) + 0x10 ];
  416.       }
  417.     }
  418.     /*-------------------------------------------------------------------*/
  419.     /*  Sprite Clipping                                                  */
  420.     /*-------------------------------------------------------------------*/
  421.     if ( !( PPU_R1 & R1_CLIP_SP ) )
  422.     {
  423.       WORD *pPointTop;
  424. #if WORKFRAME_DEFINE == WORKFRAME_NONE
  425.    pPointTop = WorkLine;
  426. #else
  427.    pPointTop = &WorkFrame[ PPU_Scanline * NES_DISP_WIDTH ];
  428. #endif
  429.       InfoNES_MemorySet( pPointTop, 0, 8 << 1 );
  430.     }
  431.     if ( nSprCnt >= 8 )
  432.       PPU_R2 |= R2_MAX_SP;  // Set a flag of maximum sprites on scanline
  433.   }
  434. }
  435. /*===================================================================*/
  436. /*                                                                   */
  437. /* InfoNES_GetSprHitY() : Get a position of scanline hits sprite #0  */
  438. /*                                                                   */
  439. /*===================================================================*/
  440. void InfoNES_GetSprHitY()
  441. {
  442. /*
  443. * Get a position of scanline hits sprite #0
  444. *
  445. */
  446.   int nYBit;
  447. int nLine;
  448. #ifdef RAM_LACK
  449.   BYTE *pbyChrData;
  450. #else
  451.   DWORD *pdwChrData;
  452. #endif
  453.   int nOff;
  454.   if ( SPRRAM[ SPR_ATTR ] & SPR_ATTR_V_FLIP )
  455.   {
  456.     // Vertical flip
  457.     nYBit = ( PPU_SP_Height - 1 );
  458. #ifdef RAM_LACK
  459.     nOff = -1;
  460. #else
  461.     nYBit <<= 3;
  462.     nOff = -2;
  463. #endif /* RAM_LACK */
  464.   }
  465.   else
  466.   {
  467.     // Non flip
  468.     nYBit = 0;
  469. #ifdef RAM_LACK
  470.     nOff = 1;
  471. #else
  472.     nOff = 2;
  473. #endif /* RAM_LACK */
  474.   }
  475.   if ( PPU_R0 & R0_SP_SIZE )
  476.   {
  477.     // Sprite size 8x16
  478.     if ( SPRRAM[ SPR_CHR ] & 1 )
  479.     {
  480. #ifdef RAM_LACK
  481.       pbyChrData = ( PPUBANK[ 4 ] + ( ( SPRRAM[ SPR_CHR ] & 0xfe ) << 4 ) + ((nYBit & 0x8) << 1) + (nYBit & 0x7) );
  482. #else
  483.       pdwChrData = (DWORD *)( ChrBuf + 256 * 64 + ( ( SPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit );
  484. #endif /* RAM_LACK */
  485.     }
  486.     else
  487.     {
  488. #ifdef RAM_LACK
  489.       pbyChrData = ( PPUBANK[ 0 ] + ( ( SPRRAM[ SPR_CHR ] & 0xfe ) << 4 ) + ((nYBit & 0x8) << 1) + (nYBit & 0x7) );
  490. #else
  491.       pdwChrData = (DWORD * )( ChrBuf + ( ( SPRRAM[ SPR_CHR ] & 0xfe ) << 6 ) + nYBit );
  492. #endif /* RAM_LACK */
  493.     }
  494.   }
  495.   else
  496.   {
  497.     // Sprite size 8x8
  498. #ifdef RAM_LACK
  499.     pbyChrData = ( PPU_SP_Base + ( SPRRAM[ SPR_CHR ] << 4 ) + nYBit );
  500. #else
  501.     pdwChrData = (DWORD *)( PPU_SP_Base + ( SPRRAM[ SPR_CHR ] << 6 ) + nYBit );
  502. #endif /* RAM_LACK */
  503.   }
  504.   if ( ( SPRRAM[ SPR_Y ] + 1 <= SCAN_UNKNOWN_START ) && ( SPRRAM[SPR_Y] > 0 ) )
  505. {
  506.   for ( nLine = 0; nLine < PPU_SP_Height; nLine++ )
  507.   {
  508. #ifdef RAM_LACK
  509.    if ( pbyChrData[ 0 ] | pbyChrData[ 8 ] )
  510. #else
  511.    if ( pdwChrData[ 0 ] | pdwChrData[ 1 ] )
  512. #endif /* RAM_LACK */
  513.    {
  514.         // Scanline hits sprite #0
  515.     SpriteJustHit = SPRRAM[SPR_Y] + 1 + nLine;
  516.     nLine = SCAN_VBLANK_END;
  517.    }
  518. #ifdef RAM_LACK
  519.       if( nLine == 7)
  520.       {
  521.         pbyChrData += nOff * 9;
  522.       }
  523.       else
  524.       {
  525.         pbyChrData += nOff;
  526.       }
  527. #else
  528.    pdwChrData += nOff;
  529. #endif /* RAM_LACK */
  530.   }
  531.   } else {
  532.     // Scanline didn't hit sprite #0
  533.   SpriteJustHit = SCAN_UNKNOWN_START + 1;
  534.   }
  535. }
需要解释的是,我也是参照之前原有的处理调整成这个样的,也许不免会有Bug,比如我刚刚还修改了一处不细心导致的问题。
与此同时,有一处临时修改现在需要更正,那就是InfoNES_Mapper.h的60行,修改成:
  1. #define PATTBL(a)      ( (a) - PPUBANK[ 0 ] )
基本上有了这些,模拟器就算是完工咯。
以上就是这次移植的主要工作了。

最后不要忘记调整堆栈的大小,我就是忘了调整大小了,结果马里奥大叔都穿墙跑啦……而且还不容易发现错误在哪。

什么?有童鞋说跑得很慢?第一,请开启编译优化,建议试试速度优化O3;第二,InfoNES里有个叫FrameSkip的变量,嘿嘿……

对了,似乎这样还不能控制哪,怎么处理手柄消息呢?还记得接口函数中有个:
  1. /* Get a joypad state */
  2. void InfoNES_PadState( DWORD *pdwPad1, DWORD
  3. *pdwPad2, DWORD *pdwSystem )
  4. {
  5. }
在这里给pdwPad1和pdwPad2赋值就OK了。
具体怎么赋值?可能可以参考下图吧,我不再继续研究啦。
joy.jpg

最后是LM4F232板上可以运行的最终工程(不能控制):
InfoNES097JRC1_SDL.7z (1.27 MB)
(下载次数: 102, 2013-10-31 09:52 上传)

另外,这InfoNES,据我仔细观察,也不是百试百灵的,好像沙罗曼蛇玩着就非常不正常。
不过能在我的80MHz的小板上跑马里奥大叔,我也是非常欣慰了。

回复评论 (4)

非常不错啊  学习学习
点赞  2014-9-3 16:50
学习学习
点赞  2022-1-15 15:37

大佬十年前的水平到现在也是高手啊 ,学习学习

点赞  2022-1-27 13:07
唉过的可真快,十年前我还在学51单片机,现在终于算入行嵌入式了。如今楼主还好吗?
点赞  2023-10-1 18:31
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复