Gfx.cpp
上传用户:luhy168
上传日期:2022-01-10
资源大小:240k
文件大小:32k
源码类别:

模拟服务器

开发平台:

Visual C++

  1. //------------------------------------------------------------------------------
  2. // Name: Gfx.cpp
  3. // Desc: Holds all the graphics related functions. This includes drawing
  4. //       scanlines, directx stuff, etc.
  5. //------------------------------------------------------------------------------
  6. #include "Gfx.h"
  7. #include "Palette.h"
  8. #include "AttributeTable.h"
  9. // Global array of colors.
  10. BYTE abyColors[NUMBER_OF_COLORS][4];
  11. // DirectDraw Surfaces used for the graphics engine.
  12. LPDIRECTDRAW7        lpDD;
  13. LPDIRECTDRAWSURFACE7 lpddsPrimary;
  14. LPDIRECTDRAWSURFACE7 lpddsBack;
  15. LPDIRECTDRAWSURFACE7 lpddsScreen;
  16. LPDIRECTDRAWCLIPPER  lpClipper;
  17. RECT                 rcScreen;
  18. BYTE* pSurfaceMemory; // Pointer to our locked memory to draw our pixels on.
  19. LONG  lBytesPerPixel; // Number of bytes per pixel.
  20. LONG  FourMinusBPP;   // 4 - Number of bytes per pixel (to speed up putpixel loop).
  21. LONG  lSurfacePitch;  // The pitch of the scanline use to move to the next line.
  22. //------------------------------------------------------------------------------
  23. // Name: CreateDirectDraw()
  24. // Desc: Creates DirectDraw and all the surfaces and sets up the colors
  25. //       array with the paletted nes colors.
  26. //------------------------------------------------------------------------------
  27. HRESULT CreateDirectDraw(HWND hwnd)
  28. {
  29. DDSURFACEDESC2 ddsd;
  30. DDPIXELFORMAT DDpf;  // DirectDraw pixel format structure.
  31. HRESULT ddrval;
  32. RECT rcClient;
  33.    
  34. // Create the main DirectDraw object.
  35. ddrval = DirectDrawCreateEx(NULL, (VOID**)&lpDD, IID_IDirectDraw7, NULL);
  36. if(ddrval != DD_OK)
  37. return FALSE;
  38. // Using DDSCL_NORMAL means we will coexist with GDI.
  39. ddrval = lpDD->SetCooperativeLevel(hwnd, DDSCL_NORMAL);
  40. if(ddrval != DD_OK)
  41. {
  42. lpDD->Release();
  43. return FALSE;
  44. }
  45. memset(&ddsd, 0, sizeof(ddsd));
  46. ddsd.dwSize = sizeof(ddsd);
  47. ddsd.dwFlags = DDSD_CAPS;
  48. ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
  49. // The primary surface is not a page flipping surface.
  50. ddrval = lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL);
  51. if (ddrval != DD_OK)
  52. {
  53. lpDD->Release();
  54. return FALSE;
  55. }
  56. // Create a clipper to ensure that our drawing stays inside our window.
  57. ddrval = lpDD->CreateClipper(0, &lpClipper, NULL);
  58. if (ddrval != DD_OK)
  59. {
  60. lpddsPrimary->Release();
  61. lpDD->Release();
  62. return FALSE;
  63. }
  64. // Setting it to our hwnd gives the clipper the coordinates from our window.
  65. ddrval = lpClipper->SetHWnd(0, hwnd);
  66. if (ddrval != DD_OK)
  67. {
  68. lpClipper-> Release();
  69. lpddsPrimary->Release();
  70. lpDD->Release();
  71. return FALSE;
  72. }
  73. // Attach the clipper to the primary surface.
  74. ddrval = lpddsPrimary->SetClipper(lpClipper);
  75. if (ddrval != DD_OK)
  76. {
  77. lpClipper-> Release();
  78. lpddsPrimary->Release();
  79. lpDD->Release();
  80. return FALSE;
  81. }
  82. GetClientRect(hwnd, &rcClient);
  83. memset(&ddsd, 0, sizeof(ddsd));
  84. ddsd.dwSize = sizeof(ddsd);
  85. ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
  86. ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
  87. ddsd.dwWidth = rcClient.right - rcClient.left;
  88. ddsd.dwHeight = rcClient.bottom - rcClient.top;
  89. // Create the backbuffer separately.
  90. ddrval = lpDD->CreateSurface(&ddsd, &lpddsBack, NULL);
  91. if (ddrval != DD_OK)
  92. {
  93. lpClipper-> Release();
  94. lpddsPrimary->Release();
  95. lpDD->Release();
  96. return FALSE;
  97. }
  98. ddsd.dwWidth = SCREEN_WIDTH;
  99. ddsd.dwHeight = SCREEN_HEIGHT;
  100. // Create the offscreen surface for drawing our screen to.
  101. ddrval = lpDD->CreateSurface(&ddsd, &lpddsScreen, NULL);
  102. if (ddrval != DD_OK)
  103. {
  104. lpClipper-> Release();
  105. lpddsPrimary->Release();
  106. lpddsBack->Release();
  107. lpDD->Release();
  108. return FALSE;
  109. }
  110. // Fill the DDpf structure and get the number of bytes per pixel.
  111. ZeroMemory(&DDpf, sizeof(DDpf));
  112. DDpf.dwSize = sizeof(DDpf);
  113. lpddsScreen->GetPixelFormat(&DDpf);
  114. // Save the number of bytes per pixel and 4 - the number of bytes
  115. // per pixel to speed things up when drawing pixels.
  116. lBytesPerPixel = DDpf.dwRGBBitCount / 8;
  117. FourMinusBPP = 4 - lBytesPerPixel;
  118. // Compile all the pixels.
  119. for (int i = 0; i < NUMBER_OF_COLORS; i++)
  120. CompilePixel(lpddsScreen, i, abyNESPalette[(3*i)], abyNESPalette[(3*i)+1], abyNESPalette[(3*i)+2]);
  121. return TRUE;
  122. } // end CreateDirectDraw()
  123. //------------------------------------------------------------------------------
  124. // Name: DestroyDirectDraw()
  125. // Desc: Cleans up everything that was initialized for DirectDdraw.
  126. //------------------------------------------------------------------------------
  127. HRESULT DestroyDirectDraw()
  128. {
  129. if(lpDD != NULL)
  130. {
  131. if (lpddsPrimary != NULL)
  132. {
  133. lpddsPrimary->Release();
  134. lpddsPrimary = NULL;
  135. }
  136. if (lpddsBack != NULL)
  137. {
  138. lpddsBack->Release();
  139. lpddsBack = NULL;
  140. }
  141. if (lpddsScreen != NULL)
  142. {
  143. lpddsScreen->Release();
  144. lpddsScreen = NULL;
  145. }
  146. if (lpClipper != NULL)
  147. {
  148. lpClipper->Release();
  149. lpClipper = NULL;
  150. }
  151. lpDD->Release();
  152. lpDD = NULL;
  153. }
  154. return DD_OK;
  155. } // end DestroyDirectDraw()
  156. //------------------------------------------------------------------------------
  157. // Name: Flip()
  158. // Desc: Blts the nes screen surface to the back surface and then flips
  159. //       the back surface and the primary if in full screen mode. If in
  160. //       windowed mode, it blts the back surface to the primary surface
  161. //       instead of fliping them.
  162. //------------------------------------------------------------------------------
  163. VOID Flip()
  164. {
  165. //lpddsBack->Blt(NULL, lpddsScreen, NULL, DDBLT_WAIT, NULL);
  166. //lpddsPrimary->Blt(NULL, lpddsBack, NULL, DDBLT_WAIT, NULL);
  167. lpddsPrimary->Blt(&rcScreen, lpddsScreen, NULL, DDBLT_WAIT, NULL);
  168. } // end Flip()
  169. //------------------------------------------------------------------------------
  170. // Name: BeginDrawing()
  171. // Desc: Clears the surface to the background color, locks the surface and
  172. //       sets up the pointer to the video memory.
  173. //------------------------------------------------------------------------------
  174. HRESULT BeginDrawing()
  175. {
  176. HRESULT ddrval;
  177. DDSURFACEDESC2 ddsdLockedSurf; // Surface description to lock our surface.
  178. // Clear to the background color which is located in bits 7-5 in reg $2001
  179. // when bit 0 is set, otherwise is the first entry in the background palette.
  180. //BYTE byColorIndex = ((CPU.Memory[0x2001] & 0xE0) >> 5) * 3;
  181. //ClearScreen(RGB(abyNESPalette[byColorIndex], 0, 0));
  182. ClearScreen(RGB(0, 0, 0));
  183. // Lock the surface.
  184. ddsdLockedSurf.dwSize = sizeof(DDSURFACEDESC2);
  185. ddrval = lpddsScreen->Lock(NULL, &ddsdLockedSurf, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL);
  186. if (ddrval == DD_OK)
  187. {
  188. // Initialize the pointer to surface memory to point to the
  189. // beginning of our scanline.
  190. pSurfaceMemory = (BYTE*)ddsdLockedSurf.lpSurface;
  191. // Save the pitch of the surface for later use in other functions.
  192. lSurfacePitch = ddsdLockedSurf.lPitch;
  193. }
  194. return ddrval;
  195. } // end BeginDrawing()
  196. //------------------------------------------------------------------------------
  197. // Name: EndDrawing()
  198. // Desc: Unlocks the drawing surface and flips the surfaces.
  199. //------------------------------------------------------------------------------
  200. VOID EndDrawing()
  201. {
  202. // Unlock the drawing surface.
  203. lpddsScreen->Unlock(NULL);
  204. // Flip the surfaces to display what we've drawn.
  205. Flip();
  206. } // end EndDrawing()
  207. //------------------------------------------------------------------------------
  208. // Name: DrawScanline()
  209. // Desc: The main routine for drawing a scanline. Locks and unlocks the
  210. //       surface and calls the scanline drawing routing for the type
  211. //       of mirroring.
  212. //------------------------------------------------------------------------------
  213. VOID DrawScanline()
  214. {
  215. // Save the pointer to video memory so we don't permanently modify it.
  216. BYTE* pTemp = pSurfaceMemory;
  217. // Now move the pointer to surface memory to the correct scanline.
  218. pSurfaceMemory += (lSurfacePitch * wScanline);
  219. // Draw our scanline if its enabled.
  220. if (CPU.Memory[0x2001] & 0x08)
  221. DrawScanlineH();
  222. // Draw all the sprites that are on the scanline if they are enabled.
  223. //if (CPU.Memory[0x2001] & 0x10)
  224. // DrawScanlineSprites();
  225. // Restore our pointer to memory.
  226. pSurfaceMemory = pTemp;
  227. } // end DrawScanline()
  228. //------------------------------------------------------------------------------
  229. // Name: DrawScanlineH()
  230. // Desc: Draws a scanline using horizontal mirroring.
  231. //------------------------------------------------------------------------------
  232. VOID DrawScanlineH()
  233. {
  234. BYTE*  pabyNameTables;
  235. BYTE*  pbyNameTableTile;
  236. BYTE*  pbyAttributeTable;
  237. BYTE*  pbyCurPatternTableAddr;
  238. BYTE   byCounter = 8;
  239. BYTE   byUpperColor = 0;
  240. WORD   wTileNum = 0;
  241. WORD   wBegTileNum = 0;
  242. WORD   relx = byHScroll[0];
  243. WORD   x = 0;
  244. DWORD  byScanlineMOD8 = wScanline % 8;
  245. __asm
  246. {
  247. // Save the registers
  248. pushad
  249. // The pointer to the current nametable tile will always
  250. // start with the first name table.
  251. // Get the bits that determine what nametable to use.
  252. xor ebx, ebx
  253. mov bl, [CPU.Memory+02000h]
  254. and bl, 03
  255. // Get the address of the name table and save it for later.
  256. lea eax, [PPU.apbyNameTables+ebx*4]
  257. mov pabyNameTables, eax
  258. // Get the pointer to the current name table and store
  259. // it in the variables for later. Also, store the
  260. // the attribute table.
  261. mov eax, [eax]
  262. mov pbyNameTableTile, eax
  263. mov pbyAttributeTable, eax
  264. add eax, 03C0h
  265. mov pbyAttributeTable, eax
  266. // Get the address of the background pattern table
  267. xor ebx, ebx
  268. mov bl, BYTE PTR [CPU.Memory+02000h]   // Address of the PPU control register #1
  269. and bl, PATTERN_TABLE_ADDRESS
  270. shr bx, 2                              // Which pattern table (0,1)
  271. mov ebx, [PPU.apbyPatternTables+ebx]   // Move the address of the pattern table array into ebx
  272. mov pbyCurPatternTableAddr, ebx        // Save the address of the pattern table for later
  273. //--------------------------------------------------------
  274. // Get the address of the tile number in the ppu memory
  275. // by (TILENUM * 16) + PATTERNTABLE (Yoshi)
  276. //--------------------------------------------------------
  277. and eax, 000000FFh
  278. mov ax, wScanline
  279. mov cx, relx
  280. // TILENUM = ((Scanline / 8) * 32) + (HScroll / 8)
  281. shr ax, 3        // Scanline / 8
  282. shl ax, 5        // Multiply by 32
  283. // Save the beginning tile number so we don't have
  284. // to recalculate it when we change name tables.
  285. mov wBegTileNum, ax
  286. shr cx, 3        // HScroll / 8
  287. add ax, cx       // ax=TILENUM
  288. // Save the tile number so we can increment it.
  289. mov wTileNum, ax
  290. // Now add the tile offset to the base address of the
  291. // name table to get the address of the tile
  292. add pbyNameTableTile, eax
  293. // Get the upper color of the tile. This is done by
  294. // getting the attribute byte and then ANDing the byte
  295. // by the correct bits for the tile (see yoshi's nes doc).
  296. // Rather that doing a series of calculations to find the
  297. // attribute offset and what bits to and the byte with, 
  298. // all that info is stored in lookup table arrays.
  299.    xor ebx, ebx
  300. mov ecx, pbyAttributeTable
  301. mov bl, [AttributeBytes+eax] // bl=offset into the attribute table
  302. mov dl, [ecx+ebx]            // dl=attribute byte
  303. mov dh, [AttributeBits+eax]  // dh=attribute bits to AND the attribute byte by
  304. mov cl, [UpColRotateVal+eax] // cl=number of bits to rotate the upper color by
  305. and dl, dh                   // dl=upper color for the tile but in the right bit position
  306. ror dl, cl                   // dl=upper color of the tile
  307. mov byUpperColor, dl
  308. // TILEADDRESS = (TILENUM * 16) + PATTERNTABLE
  309. xor eax, eax
  310. mov ecx, pbyNameTableTile
  311. mov al, BYTE PTR [ecx]
  312. shl ax, 4
  313. add eax, pbyCurPatternTableAddr // eax = TILEADDRESS
  314. // SCANLINE_ADDRESS = TILEADDRESS + (Scanline % 8)
  315. add eax, byScanlineMOD8 // eax=SCANLINE_ADDRESS
  316. // Adjust the tile for horizontal scrolling
  317. xor ecx, ecx
  318. mov cx, relx
  319. and cx, 7
  320. mov edi, 8
  321. sub edi, ecx
  322. mov bl, [eax]
  323. mov dl, [eax+8]
  324. rcl bl, cl
  325. rcl dl, cl
  326. jmp dsh_SkipForFirstTile
  327. //-----------------------------
  328. // Draw the piece of the tile
  329. //-----------------------------
  330. dsh_DrawTileBeginning:
  331. // Move the pattern table bytes into registers.
  332. mov bl, [eax]
  333. mov dl, [eax+8]
  334. dsh_SkipForFirstTile:
  335. // Move the x coordinate into a register to speed up things.
  336. mov si, x
  337. dsh_DrawTileLoop:
  338. // Calculate the color of the pixel, where eax
  339. // points to the memory location of the pattern table
  340. // tile being used.
  341. rcl bl, 1
  342. setc cl
  343. rcl dl, 1
  344. setc al
  345. shl al, 1
  346. or cl, al
  347. // Draw the pixel if the color is not 0
  348. jz dsh_DontDrawPixel
  349. // Add the upper part of the color to the color.
  350. // NOW bl=The index into the NES's palette.
  351. or cl, byUpperColor
  352. // Save all our registers so they don't get modified
  353. // in the put pixel routine.
  354. push eax
  355. push ebx
  356. push edx
  357. push esi
  358. // Put the pixel on the screen surface.
  359. push ecx
  360. call PutBackgroundPixel
  361. // Restore all our saved registers.
  362. pop esi
  363. pop edx
  364. pop ebx
  365. pop eax
  366. dsh_DontDrawPixel:
  367. // Advance the pointer to video memory to the next pixel.
  368. mov ecx, lBytesPerPixel
  369. add pSurfaceMemory, ecx
  370. // Move our position indicators to the next pixel.
  371. inc si
  372. inc relx
  373. // Check to see if were done with the scanline
  374. cmp si, SCREEN_WIDTH
  375. jge dsh_Done
  376. // Decrement our counter by 1, when its zero that means we've
  377. // drawn all the pixels for that tile on the current scanline.
  378. dec edi
  379. jnz dsh_DrawTileLoop
  380. // Restore the x variable.
  381. mov x, si
  382. // We may need to change name tables during the process
  383. cmp relx, SCREEN_WIDTH
  384. jge dsh_NewNameTable
  385. // Get the new tile by incrementing the pointer that
  386. // points to the current tile and then getting the 
  387. // new tile address into the pattern table, then get
  388. // the scanline address
  389. inc pbyNameTableTile
  390. inc wTileNum
  391. dsh_GetTileAddress:
  392. // Get the upper color of the tile. This is done by
  393. // getting the attribute byte and then ANDing the byte
  394. // by the correct bits for the tile (see yoshi's nes doc).
  395. // Rather that doing a series of calculations to find the
  396. // attribute offset and what bits to and the byte with, 
  397. // all that info is stored in lookup table arrays.
  398. xor ebx, ebx
  399. xor edi, edi
  400. mov di, wTileNum
  401. mov ecx, pbyAttributeTable
  402. mov bl, [AttributeBytes+edi] // bl=offset into the attribute table
  403. mov dl, [ecx+ebx]            // dl=attribute byte
  404. mov dh, [AttributeBits+edi]  // dh=attribute bits to AND the attribute byte by
  405. mov cl, [UpColRotateVal+edi] // cl=number of bits to rotate the upper color by
  406. and dl, dh                   // dl=upper color for the tile but in the right bit position
  407. ror dl, cl                   // dl=upper color of the tile
  408. mov byUpperColor, dl
  409. // Get the index into the pattern table and store it in al
  410. xor eax, eax
  411. mov ebx, pbyNameTableTile
  412. mov al, BYTE PTR [ebx]
  413. // TILEADDRESS = (TILENUM * 16) + PATTERNTABLE
  414. shl eax, 4
  415. add eax, pbyCurPatternTableAddr ; eax = TILEADDRESS
  416. // SCANLINE_ADDRESS = TILEADDRESS + (Scanline % 8)
  417. add eax, byScanlineMOD8 ; ax=SCANLINE_ADDRESS
  418. mov edi, 8
  419. jmp dsh_DrawTileBeginning
  420. dsh_NewNameTable:
  421. // Get the beggining tile number that we computed at the
  422. // beggining of this function since it is the same
  423. // it just uses a different tile number.
  424. // NOTE: HScroll does not play a factor in this since
  425. //       we will always start at the left of the new name table
  426. xor eax, eax
  427. mov ax, wBegTileNum
  428. mov wTileNum, ax
  429. // Now add the offset to the new name table address
  430. // to get the new tile address
  431. mov ebx, pabyNameTables
  432. add ebx, 4
  433. mov ebx, [ebx]
  434. mov pbyNameTableTile, ebx
  435. mov pbyAttributeTable, ebx
  436. add pbyAttributeTable, 03C0h
  437. add pbyNameTableTile, eax
  438. mov relx, 0
  439. xor eax, eax
  440. mov ebx, pbyNameTableTile
  441. mov al, byte ptr [ebx]    // al=TILENUM
  442. // TILEADDRESS = (TILENUM * 16) + PATTERNTABLE
  443. shl eax, 4
  444. add eax, pbyCurPatternTableAddr // eax = TILEADDRESS
  445. // SCANLINE_ADDRESS = TILEADDRESS + (Scanline % 8)
  446. add eax, byScanlineMOD8 // ax=SCANLINE_ADDRESS
  447. // Get the upper color of the tile. This is done by
  448. // getting the attribute byte and then ANDing the byte
  449. // by the correct bits for the tile (see yoshi's nes doc).
  450. // Rather that doing a series of calculations to find the
  451. // attribute offset and what bits to and the byte with, 
  452. // all that info is stored in lookup table arrays.
  453. xor ebx, ebx
  454. xor edi, edi
  455. mov di, wTileNum
  456. mov ecx, pbyAttributeTable
  457. mov bl, [AttributeBytes+edi] // bl=offset into the name table
  458. add ecx, ebx                 // now points to attribute byte for the tile
  459. mov dl, [ecx]                // dl=attribute byte
  460. mov dh, [AttributeBits+edi]  // dh=attribute bits to AND the attribute byte by
  461. and dl, dh                   // dl=upper color for the tile but in the right bit position
  462. mov cl, [UpColRotateVal+edi] // cl=number of bits to rotate the upper color by
  463. ror dl, cl                   // dl=upper color of the tile
  464. mov byUpperColor, dl
  465. mov edi, 8
  466. jmp dsh_DrawTileBeginning
  467. dsh_Done:
  468. popad
  469. }
  470. return;
  471. } // end DrawScanlineH()
  472. //------------------------------------------------------------------------------
  473. // Name: DoSprite0()
  474. // Desc: Sets the sprite #0 flag when the scanline equals the y coordinate
  475. //       of sprite #0
  476. //------------------------------------------------------------------------------
  477. VOID DoSprite0()
  478. {
  479. // If the scanline = the y coordinate of sprite #0, then
  480. // set bit #6 of register $2002
  481. if (wScanline == (abySPRRAM[0] + 8))
  482. CPU.Memory[0x2002] |= 0x40;
  483. } // end DoSprite0()
  484. //------------------------------------------------------------------------------
  485. // Name: DrawScanlineSprites()
  486. // Desc: Draws all the sprites that on the current scanline.
  487. //------------------------------------------------------------------------------
  488. VOID DrawScanlineSprites()
  489. {
  490. // Draw Sprite #0 separatly from the rest of the sprites
  491. // becuase we have to check for the Sprite #0 hit flag.
  492. DoSprite0();
  493. // Loop through the rest of the sprites and draw each
  494. // scanline of the sprite.
  495. for (int i = 0; i < 63; i++)
  496. DrawSpriteLine(i);
  497. } // end DrawScanlineSprites()
  498. //------------------------------------------------------------------------------
  499. // Name: DrawSprites()
  500. // Desc: Draws all the sprites either in front or behind the background.
  501. //------------------------------------------------------------------------------
  502. VOID DrawSprites(BYTE byPriority)
  503. {
  504. // Loop through all the sprites and display them if their
  505. // priority bit is equal to the bit passed in.
  506. if (CPU.Memory[0x2001] & 0x10)
  507. for (int i = 63; i >= 0; i--)
  508. if ((abySPRRAM[((i*4)+2)]&0x20) == byPriority)
  509. DrawSprite(i);
  510. } // end DrawSprites()
  511. //------------------------------------------------------------------------------
  512. // Name: DrawSprite()
  513. // Desc: Draws a 8x8 or 8x16 pixel sprite.
  514. //------------------------------------------------------------------------------
  515. VOID DrawSprite(BYTE bySpriteNum)
  516. {
  517. BYTE  byColor;             // Index into the NES palette for the sprite.
  518. BYTE* pbyTileByte;         // Pointer to the pattern table byte.
  519. BYTE* pSaveVideoMemStart = pSurfaceMemory;
  520. // Save all the information about the sprite.
  521. BYTE byYPos = abySPRRAM[(bySpriteNum*4)] + 1;
  522. BYTE byTileNum = abySPRRAM[(bySpriteNum*4)+1];
  523. BYTE byAttributes = abySPRRAM[(bySpriteNum*4)+2];
  524. BYTE byXPos = abySPRRAM[(bySpriteNum*4)+3];
  525. BYTE byUpperColor = (byAttributes & 0x3) << 2;
  526. // Get the pointer to the tile byte in the pattern table for the sprite.
  527. pbyTileByte = (byTileNum * 16) + PPU.apbyPatternTables[(CPU.Memory[0x2000]&0x08)>>3];
  528. // Move the video memory pointer to where the sprite is supposed to be horizontally.
  529. if (byAttributes & 0x40)
  530. pSurfaceMemory += (byXPos * lBytesPerPixel) + (7 * lBytesPerPixel);
  531. else
  532.         pSurfaceMemory += byXPos * lBytesPerPixel;
  533. // Move the video memory pointer to where the sprite is supposed to be vertically.
  534. if (byAttributes & 0x80)
  535. pSurfaceMemory += ((byYPos + 7) * lSurfacePitch);
  536. else
  537.         pSurfaceMemory += byYPos * lSurfacePitch;
  538. // Draw the sprite by drawing each tile line then moving to the next byte.
  539. for (int i = 0; i < 8; i++)
  540. {
  541. BYTE  byTestBit = 0x80;    // Color testing bit for the sprite tile.
  542. BYTE  byColorShiftVal = 7; // Number of bits to shift the color by.
  543. while (byTestBit != 0)
  544. {
  545. // The color is the to lower bits from the pattern table
  546. // obtained in the same way as a background tile plus the
  547. // two uppercolor bits in the lower 2 bits of the attribute
  548. // byte.
  549. byColor = ((*pbyTileByte & byTestBit) >> byColorShiftVal) |
  550. (((*(pbyTileByte+8) & byTestBit) >> byColorShiftVal) << 1);
  551. // Draw the pixel on the screen if the color is not 0.
  552. if (byColor != 0)
  553. PutSpritePixel(byColor | byUpperColor);
  554. // Move to the next pixel.
  555. if (byAttributes & 0x40)
  556. pSurfaceMemory -= lBytesPerPixel;
  557. else
  558. pSurfaceMemory += lBytesPerPixel;
  559. byColorShiftVal--;
  560. byTestBit >>= 1;
  561. }
  562. // Reset the horizontal video position for the next line.
  563. if (byAttributes & 0x40)
  564. pSurfaceMemory += (8 * lBytesPerPixel);
  565. else
  566. pSurfaceMemory -= (8 * lBytesPerPixel);
  567. // Move to the next line.
  568. if (byAttributes & 0x80)
  569. pSurfaceMemory -= lSurfacePitch;
  570. else
  571. pSurfaceMemory += lSurfacePitch;
  572. // Move to the next byte in the sprite.
  573. pbyTileByte++;
  574. }
  575. // Restore the surface memory pointer to the beginning
  576. // of the scanline.
  577. pSurfaceMemory = pSaveVideoMemStart;
  578. //-------------------------------------------------------------------------
  579. // Draw the second part of the sprite if its a 8x16 sprite.
  580. // Thats located in in bit 5 of reg $2000.
  581. //-------------------------------------------------------------------------
  582. if (CPU.Memory[0x2000] & 0x20)
  583. {
  584. BYTE  byColor;             // Index into the NES palette for the sprite.
  585. BYTE* pbyTileByte;         // Pointer to the pattern table byte.
  586. BYTE* pSaveVideoMemStart = pSurfaceMemory;
  587. // Save all the information about the sprite.
  588. BYTE byYPos = abySPRRAM[(bySpriteNum*4)] + 8;
  589. BYTE byTileNum = abySPRRAM[(bySpriteNum*4)+1] + 1;
  590. BYTE byAttributes = abySPRRAM[(bySpriteNum*4)+2];
  591. BYTE byXPos = abySPRRAM[(bySpriteNum*4)+3];
  592. BYTE byUpperColor = (byAttributes & 0x3) << 2;
  593. // Get the pointer to the tile byte in the pattern table for the sprite.
  594. pbyTileByte = (byTileNum * 16) + PPU.apbyPatternTables[(CPU.Memory[0x2000]&0x08)>>3];
  595. // Move the video memory pointer to where the sprite is supposed to be horizontally.
  596. if (byAttributes & 0x40)
  597. pSurfaceMemory += (byXPos * lBytesPerPixel) + (8 * lBytesPerPixel);
  598. else
  599. pSurfaceMemory += byXPos * lBytesPerPixel;
  600. // Move the video memory pointer to where the sprite is supposed to be vertically.
  601. if (byAttributes & 0x80)
  602. pSurfaceMemory += ((byYPos + 8) * lSurfacePitch);
  603. else
  604. pSurfaceMemory += byYPos * lSurfacePitch;
  605. // Draw the sprite by drawing each tile line then moving to the next byte.
  606. for (int i = 0; i < 8; i++)
  607. {
  608. BYTE  byTestBit = 0x80;    // Color testing bit for the sprite tile.
  609. BYTE  byColorShiftVal = 7; // Number of bits to shift the color by.
  610. while (byTestBit != 0)
  611. {
  612. // The color is the to lower bits from the pattern table
  613. // obtained in the same way as a background tile plus the
  614. // two uppercolor bits in the lower 2 bits of the attribute
  615. // byte.
  616. byColor = ((*pbyTileByte & byTestBit) >> byColorShiftVal) |
  617. (((*(pbyTileByte+8) & byTestBit) >> byColorShiftVal) << 1);
  618. // Draw the pixel on the screen if the color is not 0.
  619. if (byColor != 0)
  620. PutSpritePixel(byColor | byUpperColor);
  621. // Move to the next pixel.
  622. if (byAttributes & 0x40)
  623. pSurfaceMemory -= lBytesPerPixel;
  624. else
  625. pSurfaceMemory += lBytesPerPixel;
  626. byColorShiftVal--;
  627. byTestBit >>= 1;
  628. }
  629. // Reset the horizontal video position for the next line.
  630. if (byAttributes & 0x40)
  631. pSurfaceMemory += (8 * lBytesPerPixel);
  632. else
  633. pSurfaceMemory -= (8 * lBytesPerPixel);
  634. // Move to the next line.
  635. if (byAttributes & 0x80)
  636. pSurfaceMemory -= lSurfacePitch;
  637. else
  638. pSurfaceMemory += lSurfacePitch;
  639. // Move to the next byte in the sprite.
  640. pbyTileByte++;
  641. }
  642. // Restore the surface memory pointer to the beginning
  643. // of the scanline.
  644. pSurfaceMemory = pSaveVideoMemStart;
  645. }
  646. } // end DrawSprite()
  647. //------------------------------------------------------------------------------
  648. // Name: DrawSpriteLine()
  649. // Desc: Draws a 8 pixel scanline for the sprite.
  650. //------------------------------------------------------------------------------
  651. VOID DrawSpriteLine(BYTE bySpriteNum)
  652. {
  653. BYTE  byTestBit = 0x80;    // Color testing bit for the sprite tile.
  654. BYTE  byColorShiftVal = 7; // Number of bits to shift the color by.
  655. BYTE  byColor;             // Index into the NES palette for the sprite.
  656. BYTE* pbyScanlineByte;     // Pointer to the pattern table byte.
  657. BYTE* pSaveVideoMemStart = pSurfaceMemory;
  658. // Save all the information about the sprite.
  659. BYTE byYPos = abySPRRAM[(bySpriteNum*4)];
  660. BYTE byTileNum = abySPRRAM[(bySpriteNum*4)+1];
  661. BYTE byAttributes = abySPRRAM[(bySpriteNum*4)+2];
  662. BYTE byXPos = abySPRRAM[(bySpriteNum*4)+3];
  663. BYTE byUpperColor = (byAttributes & 0x3) << 2;
  664. // If one of the sprites scanlines is on this screen scaline,
  665. // then we have to draw that sprite scanline.
  666. if ((unsigned)(wScanline - byYPos) < 8)
  667. {
  668. // Get the pointer to the scanline byte in the pattern table for the sprite.
  669. pbyScanlineByte = (byTileNum * 16) + PPU.apbyPatternTables[(CPU.Memory[0x2000]&0x08)>>3];
  670. pbyScanlineByte += (wScanline - byYPos);
  671. // Move the video memory pointer to where the sprite is supposed to be.
  672. if (byAttributes & 0x40)
  673. pSurfaceMemory += (byXPos * lBytesPerPixel) + (7 * lBytesPerPixel);
  674. else
  675.             pSurfaceMemory += byXPos * lBytesPerPixel;
  676. // Draw the sprite scanline.
  677. while (byTestBit != 0)
  678. {
  679. // The color is the to lower bits from the pattern table
  680. // obtained in the same way as a background tile plus the
  681. // two uppercolor bits in the lower 2 bits of the attribute
  682. // byte.
  683. byColor = ((*pbyScanlineByte & byTestBit) >> byColorShiftVal) |
  684. (((*(pbyScanlineByte+8) & byTestBit) >> byColorShiftVal) << 1) |
  685. byUpperColor;
  686. // Draw the pixel on the screen if the color is not 0.
  687. if (byColor != 0)
  688. PutSpritePixel(byColor);
  689. // Move to the next pixel.
  690. if (byAttributes & 0x40)
  691. pSurfaceMemory -= lBytesPerPixel;
  692. else
  693. pSurfaceMemory += lBytesPerPixel;
  694. byColorShiftVal--;
  695. byTestBit >>= 1;
  696. }
  697. // Restore the surface memory pointer to the beginning
  698. // of the scanline.
  699. pSurfaceMemory = pSaveVideoMemStart;
  700. }
  701. } // end DrawSpriteLine()
  702. //------------------------------------------------------------------------------
  703. // Name: GetMaskInfo()
  704. // Desc: Uses the color's bitmasks to calculate values which we 
  705. //       will use to compile the pixel value.
  706. //------------------------------------------------------------------------------
  707. INT GetMaskInfo(DWORD dwBitmask, int* pnShift)
  708. {
  709. int nPrecision = 0;
  710. int nShift     = 0;
  711. // Count the zeros on right hand side.
  712. while (!(dwBitmask & 0x01))
  713. {
  714. dwBitmask >>= 1;
  715. nShift++;
  716. }
  717. // Count the ones on right hand side.
  718. while (dwBitmask & 0x01)
  719. {
  720. dwBitmask >>= 1;
  721. nPrecision++;
  722. }
  723. // Save the shift value.
  724. *pnShift = nShift;
  725. // Return the number of ones.
  726. return nPrecision;
  727. } // end GetMaskInfo()
  728. //------------------------------------------------------------------------------
  729. // Name: CompilePixel()
  730. // Desc: Stores the four bytes corresponding to the compiled pixel 
  731. //       value in a class variable.
  732. //------------------------------------------------------------------------------
  733. VOID CompilePixel(LPDIRECTDRAWSURFACE7 lpSurf, int nColorIndex, int r, int g, int b)
  734. {
  735. DDPIXELFORMAT DDpf;    // DirectDraw pixel format structure.
  736. int rsz, gsz, bsz;     // Bitsize of field.
  737. int rsh, gsh, bsh;    // 0抯 on left (the shift value).
  738. DWORD dwCompiledPixel; // Our pixel with r,g,b values compiled.
  739. // Get the pixel format.
  740. ZeroMemory (&DDpf, sizeof(DDpf));
  741. DDpf.dwSize = sizeof(DDpf);
  742. lpSurf->GetPixelFormat(&DDpf);
  743. // Get the shift and precision values and store them.
  744. rsz = GetMaskInfo(DDpf.dwRBitMask, &rsh);
  745. gsz = GetMaskInfo(DDpf.dwGBitMask, &gsh);
  746. bsz = GetMaskInfo(DDpf.dwBBitMask, &bsh);
  747. // Keep only the MSB bits of component.
  748. r >>= (8-rsz);
  749. g >>= (8-gsz);
  750. b >>= (8-bsz);
  751. // Shift them into place.
  752. r <<= rsh;
  753. g <<= gsh;
  754. b <<= bsh;
  755. // Store the compiled pixel.
  756. dwCompiledPixel = (DWORD)(r | g | b);
  757. abyColors[nColorIndex][3] = (BYTE)(dwCompiledPixel);
  758. abyColors[nColorIndex][2] = (BYTE)(dwCompiledPixel >>= 8);
  759. abyColors[nColorIndex][1] = (BYTE)(dwCompiledPixel >>= 8);
  760. abyColors[nColorIndex][0] = (BYTE)(dwCompiledPixel >>= 8);
  761. return;
  762. } // end CompilePixel()
  763. //------------------------------------------------------------------------------
  764. // Name: PutBackgroundPixel()
  765. // Desc: Puts a background pixel independant of the bit depth 
  766. //       on the locked surface.
  767. //------------------------------------------------------------------------------
  768. VOID __stdcall PutBackgroundPixel(BYTE nColor)
  769. {
  770. // Copy the pointer to video memory so the main one doesn't get modified.
  771. BYTE *pVideoMem = pSurfaceMemory;
  772. // Speed up the array access in the loop by storing this here.
  773. BYTE byColor = PPU.abyPalettes[nColor];
  774. // Put the number of bytes for each pixel on the surface.
  775. for (int i = 3; i >= FourMinusBPP; i--)
  776. {
  777. *pVideoMem = abyColors[byColor][i];
  778. pVideoMem++;
  779. }
  780. } // end PutBackgroundPixel()
  781. //------------------------------------------------------------------------------
  782. // Name: PutSpritePixel()
  783. // Desc: Puts a background pixel independant of the bit depth 
  784. //       on the locked surface.
  785. //------------------------------------------------------------------------------
  786. VOID __stdcall PutSpritePixel(BYTE nColor)
  787. {
  788. // Copy the pointer to video memory so the main one doesn't get modified.
  789. BYTE *pVideoMem = pSurfaceMemory;
  790. // Speed up the array access in the loop by storing this here.
  791. BYTE byColor = PPU.abyPalettes[0x10+nColor];
  792. // Put the number of bytes for each pixel on the surface.
  793. for (int i = 3; i >= FourMinusBPP; i--)
  794. {
  795. *pVideoMem = abyColors[byColor][i];
  796. pVideoMem++;
  797. }
  798. } // end PutSpritePixel()
  799. //------------------------------------------------------------------------------
  800. // Name: ClearScreen()
  801. // Desc: Clears the NES screen to the background color.
  802. //------------------------------------------------------------------------------
  803. VOID ClearScreen(COLORREF crColor)
  804. {
  805. DDBLTFX ddbltfx;
  806. // Fill the NES screen surface with the background color.
  807. ddbltfx.dwSize = sizeof(DDBLTFX);
  808. ddbltfx.dwFillColor = crColor;
  809. lpddsScreen->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);
  810. } // end ClearScreen()
  811. //------------------------------------------------------------------------------
  812. // Name: UpdateBounds()
  813. // Desc: Updates the rect for ......
  814. //------------------------------------------------------------------------------
  815. HRESULT UpdateBounds(HWND hwnd)
  816. {
  817. POINT ptWindow; // Point used for converting client to window coordinates.
  818. // Get the client rectangle.
  819. GetClientRect(hwnd, &rcScreen);
  820. // Convert the top left coordinates.
  821. ptWindow.x = rcScreen.left;
  822. ptWindow.y = rcScreen.top;
  823. ClientToScreen(hwnd, &ptWindow);
  824. rcScreen.left = ptWindow.x;
  825. rcScreen.top = ptWindow.y;
  826. // Convert the bottom right coordinates.
  827. ptWindow.x = rcScreen.right;
  828. ptWindow.y = rcScreen.bottom;
  829. ClientToScreen(hwnd, &ptWindow);
  830. rcScreen.right = ptWindow.x;
  831. rcScreen.bottom = ptWindow.y;
  832. return S_OK;
  833. } // end UpdateBounds()
  834. //------------------------------------------------------------------------------
  835. // Name: OutputText()
  836. // Desc: Outputs text to the screen surface.
  837. //------------------------------------------------------------------------------
  838. VOID OutputText(LPSTR strText, INT nX, INT nY, COLORREF cfFront, COLORREF cfBack)
  839. {
  840. HDC hdc;
  841. if (lpddsScreen->GetDC(&hdc) == DD_OK)
  842. {
  843. SetBkColor(hdc, cfBack);
  844. SetTextColor(hdc, cfFront);
  845. TextOut(hdc, nX, nY, strText, lstrlen(strText));
  846. lpddsScreen->ReleaseDC(hdc);
  847. }
  848. } // end OutputText()